home *** CD-ROM | disk | FTP | other *** search
/ Aminet 19 / Aminet 19 (1997)(GTI - Schatztruhe)[!][Jun 1997].iso / Aminet / disk / misc / NewHackdisk.lha / NewHackdisk / hackdisk.ASM next >
Assembly Source File  |  1997-03-25  |  72KB  |  3,129 lines

  1. ; FILE: Source:hackdisk.ASM          REV: 8 --- hackdisk.device
  2.  
  3. ********************************************************************************
  4. * hackdisk.ASM -- my very own version of trackdisk.device
  5. * hackdisk.device - Copyright © 1992,93 by Dan Babcock,
  6. * hackdisk.device - Copyright © 1996-1997 by Harry Sintonen.
  7. *
  8. * Version history:
  9. * V1.00 04/25/92 First version. Supports 880K drives only.
  10. * V1.01 04/28/92 Optimized CopyMem
  11. * V1.02 04/30/92 More intelligent write algorithm fixes performance problem
  12. * V1.03 05/03/92 Sets IO_ACTUAL
  13. * V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
  14. *                errors are only reported when it makes sense.
  15. *                CrossDOSV5/CrossPC flushed out these bugs.
  16. * V1.10 07/04/92 Compatible with Kickstart 1.2 and higher.
  17. *                Fixed Seek (IO_OFFSET is in bytes, not tracks).
  18. *                Released on Fish 697.
  19. * V1.11 08/03/92 Fixed bug in readtrack routine - buffer not cleared if error
  20. *                in some cases (no sync).
  21. *                Fills TC_Userdata with bogus value to fool CrossDOS under Kick
  22. *                1.2/1.3.
  23. *                Puts -1 in IO_DEVICE on open error. Some (though not many)
  24. *                applications depend on this.
  25. *                Using a slightly more creative "invalid sync word" (was 0).
  26. *                This should fix many drive incompatibility problems.
  27. *                Glitch in disk check code fixed (only affected non-NoClick mode).
  28. * V1.12 08/16/92 CMD_STOP/CMD_START should work now.
  29. * V2.00 12/26/92 Supports Chinon/Commodore FB357A 150RPM HD floppy drives.
  30. *                Support added for 5.25 inch drives (NOT TESTED!!!)
  31. *                Added track number to verify error display (only under 2.0+).
  32. *                Added a tiny module at the end (see the comments for details).
  33. *                Released on Fish 803.
  34. * V2.01 01/22/93 Bug fix: write protection detected (broke in 2.00)
  35. * V2.02 03/13/93 Now performs an AllocUnit on all units in the initroutine
  36. *
  37. * V2.03 11/06/96 Fixed one bug and turned code to compilable with DevPac 3.
  38. * V2.04 03/25/97 Aminet release.
  39. ********************************************************************************
  40.  
  41. ;This source was assembled with Devpac 3.
  42.  
  43. DEVPAC    SET    1
  44. NUMTRACKS    EQU    80
  45. SECPERTRACK    EQU    11
  46.  
  47. RETRYCNT    EQU    3
  48.  
  49.  
  50.     IFD    DEVPAC
  51.     incdir    "include:"
  52.     include    "exec/types.i"
  53.     include    "exec/exec.i"
  54.     include    "hardware/custom.i"
  55.     include    "hardware/cia.i"
  56.     include    "hardware/dmabits.i"
  57.     include    "hardware/intbits.i"
  58.     include    "hardware/adkbits.i"
  59.     include    "devices/timer.i"
  60.     include    "devices/trackdisk.i"
  61.     include    "resources/disk.i"
  62.  
  63. ;;    include    "intuition/intuition.i"
  64.     IFND    es_SIZEOF
  65. es_SIZEOF    EQU    20
  66.     ENDC
  67.  
  68.     include    "exec/exec_lib.i"
  69.     include    "libraries/graphics_lib.i"
  70.     include    "libraries/intuition_lib.i"
  71.     IFND    _LVOSubTime
  72. _LVOSubTime    EQU    -$30
  73. _LVOGetSysTime    EQU    -$42
  74.     ENDC
  75.     IFND    _LVOAbleICR
  76. _LVOAbleICR    EQU    -$12
  77. _LVOSetICR    EQU    -$18
  78.     ENDC
  79.     IFND    IOERR_NOCMD
  80. IOERR_NOCMD    EQU    -3
  81.     ENDC
  82. push    MACRO    * ea
  83.     move.l    \1,-(sp)
  84.     ENDM
  85. pop    MACRO    * ea
  86.     move.l    (sp)+,\1
  87.     ENDM
  88.  
  89.     ELSE
  90.  
  91.     exeobj
  92.     objfile    'devs:hackdisk.device'
  93.     MC68000
  94.     multipass
  95.     ENDC
  96.  
  97. ;Standard register usage:
  98. ;A6            - ExecBase or $dff000
  99. ;A5 (A_DEVICE) - device ptr
  100. ;A4 (A_UNIT)   - unit ptr
  101. ;A3 (A_IO)     - IORequest
  102.  
  103.     IFND    SYS
  104. SYS    MACRO
  105.     jsr    _LVO\1(a6)
  106.     ENDM
  107.     ENDC
  108.  
  109.     IFND    _custom
  110. _custom    EQU    $dff000
  111.     ENDC
  112.  
  113. ;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
  114. INFO_LEVEL    EQU    0
  115.  
  116. A_DEVICE    EQUR    a5
  117. A_UNIT    EQUR    a4
  118. A_IO    EQUR    a3
  119.  
  120.  
  121. VERSION    EQU    127
  122. REVISION    EQU    0
  123. MYPRI    EQU    0
  124.  
  125. MD_NUMUNITS    EQU    4
  126. STACKSIZE    EQU    4000
  127. TASKPRI    EQU    5
  128.  
  129. ;Put a message to the serial port.  Used like so:
  130. ;
  131. ;PUTDEBUG    <'Init: called'>
  132. ;
  133. ;Parameters can be printed out by pushing them on the stack and
  134. ;adding the appropriate C printf-style % formatting commands.
  135.  
  136. PUTDEBUG     MACRO    ;<msg>
  137.     IFNE    INFO_LEVEL
  138.     movem.l    d0-d1/a0-a1,-(sp)
  139.     lea    .msg\@(pc),a0    ;Point to static format string
  140.     lea    16(sp),a1    ;Point to args
  141.     bsr    KPutFmt
  142.     movem.l    (sp)+,d0-d1/a0-a1
  143.     bra    .end\@
  144. .msg\@:
  145.     dc.b    \1,$a,0
  146.     even
  147. .end\@:
  148.     ENDC
  149.     ENDM
  150.  
  151. *--------------------------------------------------------------------
  152. *
  153. * Driver error defines
  154. *
  155. *--------------------------------------------------------------------
  156.  
  157. ;TDERR_NotSpecified    EQU    20    ; general catchall
  158. ;TDERR_NoSecHdr        EQU    21    ; couldn't even find a sector
  159. ;TDERR_BadSecPreamble    EQU    22    ; sector looked wrong
  160. ;TDERR_BadSecID        EQU    23    ; ditto
  161. ;TDERR_BadHdrSum    EQU    24    ; header had incorrect checksum
  162. ;TDERR_BadSecSum    EQU    25    ; data had incorrect checksum
  163. ;TDERR_TooFewSecs    EQU    26    ; couldn't find enough sectors
  164. ;TDERR_BadSecHdr    EQU    27    ; another "sector looked wrong"
  165. ;TDERR_WriteProt    EQU    28    ; can't write to a protected disk
  166. ;TDERR_DiskChanged    EQU    29    ; no disk in the drive
  167. ;TDERR_SeekError    EQU    30    ; couldn't find track 0
  168. ;TDERR_NoMem        EQU    31    ; ran out of memory
  169. ;TDERR_BadUnitNum    EQU    32    ; asked for a unit > NUMUNITS
  170. ;TDERR_BadDriveType    EQU    33    ; not a drive that trackdisk groks
  171. ;TDERR_DriveInUse    EQU    34    ; someone else allocated the drive
  172. ;TDERR_PostReset    EQU    35    ; user hit reset; awaiting doom
  173.  
  174. ; STRUCTURE TIMEVAL,0
  175. ;    ULONG    TV_SECS
  176. ;    ULONG    TV_MICRO
  177. ;    LABEL    TV_SIZE
  178.  
  179. ;Unit structure
  180.  
  181. *--------------------------------------------------------------------
  182. *
  183. * Public portion of unit structure
  184. *
  185. *--------------------------------------------------------------------
  186.  
  187. ;*------ UNIT_FLAG definitions:
  188.  
  189. ;These are bogus, but I won't re-define them.
  190. ;    BITDEF  UNIT,ACTIVE,0        ; driver is active
  191. ;    BITDEF  UNIT,INTASK,1        ; running in driver's task
  192.     BITDEF    UNIT,DiskInDrive,2
  193.     BITDEF    UNIT,WriteProtected,3
  194.  
  195. ; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
  196. ;    UWORD    TDU_COMP01TRACK        ; track for first precomp
  197. ;    UWORD    TDU_COMP10TRACK        ; track for second precomp
  198. ;    UWORD    TDU_COMP11TRACK        ; track for third precomp
  199. ;    ULONG    TDU_STEPDELAY        ; time to wait after stepping
  200. ;    ULONG    TDU_SETTLEDELAY        ; time to wait after seeking
  201. ;    UBYTE    TDU_RETRYCNT        ; # of times to retry
  202. ;    UBYTE    TDU_PUBFLAGS        ; public flags, see below
  203. ;    UWORD    TDU_CURRTRK        ; track heads are over
  204.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  205. ;    ULONG    TDU_CALIBRATEDELAY    ; time to wait after stepping
  206.                     ; for recalibrate
  207. ;    ULONG    TDU_COUNTER        ; counter for disk changes
  208.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  209. ;    LABEL    TDU_PUBLICUNITSIZE
  210. ;*--------------------------------------------------------------------
  211.  
  212.     STRUCTURE    MyUnit,TDU_PUBLICUNITSIZE
  213.     STRUCT    Drive_LastCheck,TV_SIZE    ;last time diskchange was checked
  214.     STRUCT    ChangeIntList,MLH_SIZE    ;must be initialized!!!
  215.     LONG    TDRemoveInt
  216.     BYTE    UnitNum
  217.     BYTE    unit_pad1
  218.  
  219. ;The following will be InitStruct'ed in GetDriveType
  220.     LABEL    DriveParams
  221.     BYTE    DriveType
  222.     BYTE    NumTracks
  223.     LONG    BytesPerDisk
  224.     LONG    SectorMask
  225.     WORD    GapCount
  226.     LONG    SectorsPerTrack
  227.     LONG    RawBufSize
  228.     WORD    WriteDskLen
  229.     WORD    ReadDskLen
  230.     WORD    VerifyDskLen
  231.     WORD    MaxValidSec
  232.     WORD    FirstSector
  233.     LABEL    EndDriveParams
  234.     LABEL    MyUnit_Sizeof
  235.  
  236. DriveParams_Sizeof    EQU    EndDriveParams-DriveParams
  237.     IFGT    DriveParams_Sizeof&1
  238.     FAIL    DriveParams_Sizeof MUST be word-aligned!!!
  239.     ENDC
  240.  
  241. ;*
  242. ;* Flags for TDU_PUBFLAGS:
  243. ;*
  244. ;    BITDEF    TDP,NOCLICK,0        ; set to enable noclickstart
  245.     BITDEF    TDP,VERIFY,1
  246.  
  247. ;Device global structure -- accessed as positive offsets from device base
  248. ;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
  249.     STRUCTURE    DeviceGlobals,LIB_SIZE
  250.     STRUCT    TaskPort,MP_SIZE    ;MsgPort for task
  251.     STRUCT    Task,TC_SIZE
  252.     STRUCT    TaskStack,STACKSIZE
  253.     STRUCT    TempTimeVal,TV_SIZE
  254.     LONG    SegList    ;not used for ROM version
  255.     STRUCT    TimerIORequest,IOTV_SIZE
  256.     STRUCT    TimerPort,MP_SIZE
  257.     STRUCT    LastCheck,TV_SIZE    ;last time diskchange was checked
  258.     STRUCT    DiskResourceUnit,DRU_SIZE    ;must be initialized!!!
  259.     STRUCT    DiskResourcePort,MP_SIZE    ;must be initialized!!!
  260.  
  261. ;Allocated memory pointers
  262.     LONG    RawBuffer
  263.     LONG    DecodedBuffer
  264.     LONG    VerifyBuffer
  265.  
  266.     STRUCT    SectorLabels,SECPERTRACK*2*16    22*16 !??
  267.     STRUCT    Unit0,MyUnit_Sizeof
  268.     STRUCT    Unit1,MyUnit_Sizeof
  269.     STRUCT    Unit2,MyUnit_Sizeof
  270.     STRUCT    Unit3,MyUnit_Sizeof
  271.     LONG    GraphBase
  272.     LONG    Int_Base        ;hs
  273.     LONG    DiskResourceBase
  274.     LONG    CIABase
  275.     WORD    SyncCount
  276.     WORD    IndexDskLen
  277.     WORD    BlockIntDskLen
  278.     LONG    WriteMap
  279.  
  280.     LABEL    StartSigs
  281.     LONG    SyncSig
  282.     LONG    BlockSig
  283.     LABEL    EndSigs
  284.  
  285.     BYTE    BufferTrack
  286.     BYTE    BufferDrive
  287.     BYTE    DEV_FLAGS
  288.     BYTE    MotorState
  289.     BYTE    InquireBits
  290.     LABEL    MyDev_Sizeof
  291.  
  292. NumSigs    EQU    (EndSigs-StartSigs)/4
  293.  
  294. ;Bit definitions for DEV_FLAGS:
  295.     BITDEF    DEV,Dirty,0    ;buffered track has been changed
  296.     BITDEF    DEV,Stopped,1    ;device is stopped
  297.     BITDEF    DEV,Verify,2    ;used by BlockInt for verify
  298.  
  299. ;A romtag structure.  After your driver is brought in from disk, the
  300. ;disk image will be scanned for this structure to discover magic constants
  301. ;about you (such as where to start running you from...).
  302.  
  303. RomTag:
  304.     dc.w    RTC_MATCHWORD    ;$4AFC ('illegal' opcode)
  305.     dc.l    RomTag
  306.     dc.l    ENDCode    ;pointer to end of code
  307.     dc.b    RTF_AUTOINIT+RTF_COLDSTART    ;set things up automatically
  308.     dc.b    VERSION
  309.     dc.b    NT_DEVICE    ;module type (either device or library)
  310.     dc.b    MYPRI    ;usually not important
  311.     dc.l    Name    ;name used in OpenDevice
  312.     dc.l    IDString    ;optional
  313.     dc.l    Init    ;more init info
  314.  
  315. Name:    dc.b    'trackdisk.device',0
  316.     even
  317.  
  318. IDString:    dc.b    'Hackdisk 2.04 - Copyright © 1996-1997 by Harry Sintonen,',10
  319.     dc.b    'Original Hackdisk Copyright © 1992,93 by Dan Babcock',10,0
  320.     even
  321. DiskResourceName:
  322.     dc.b    'disk.resource',0
  323. TimerName:
  324.     dc.b    'timer.device',0
  325. GraphName:
  326.     dc.b    'graphics.library',0
  327. CIAName:    dc.b    'ciab.resource',0
  328. IntName:    dc.b    'intuition.library',0
  329.     even
  330.  
  331. ;The romtag specified that we were "RTF_AUTOINIT".  This means that the
  332. ;RT_INIT structure member points to one of these tables below.  If the
  333. ;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
  334.  
  335. Init:    dc.l    MyDev_Sizeof    ;data space size (at least
  336.                 ;LIB_SIZE!!)
  337.     dc.l    FuncTable    ;pointer to function initializers
  338.     dc.l    DataTable    ;pointer to data initializers
  339.     dc.l    InitRoutine    ;routine to run
  340.  
  341. FuncTable:
  342.     dc.w    -1    ;this indicates that the following are offsets,
  343.             ;rather than absolute addresses
  344.     dc.w    Open-FuncTable    ;standard system routines
  345.     dc.w    _Close-FuncTable
  346.     dc.w    Expunge-FuncTable
  347.     dc.w    .Null-FuncTable    ;Reserved for future use!
  348.     dc.w    BeginIO-FuncTable    ;device definitions
  349.     dc.w    AbortIO-FuncTable
  350.     dc.w    -1    ;function table end marker
  351. .Null:    moveq    #0,d0
  352.     rts
  353.  
  354. ;The data table initializes static data structures. The format is
  355. ;specified in exec/InitStruct routine's manual pages.  The
  356. ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  357. ;The first argument is the offset from the device base for this
  358. ;byte/word/long. The second argument is the value to put in that cell.
  359. ;The table is null terminated
  360.  
  361. DataTable:
  362.     INITBYTE    LN_TYPE,NT_DEVICE
  363.     INITLONG    LN_NAME,Name
  364.     INITBYTE    LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
  365.     INITWORD    LIB_VERSION,VERSION
  366.     INITWORD    LIB_REVISION,REVISION
  367.     INITLONG    LIB_IDSTRING,IDString
  368.     dc.w    0    ;terminate list (only one byte needed)
  369.  
  370. CmdTable:    dc.w    Invalid-CmdTable    ;0    CMD_INVALID
  371.     dc.w    Invalid-CmdTable    ;1    CMD_RESET
  372.     dc.w    Read-CmdTable        ;2    CMD_READ
  373.     dc.w    Write-CmdTable        ;3    CMD_WRITE / ETD_
  374.     dc.w    Update-CmdTable        ;4    CMD_UPDATE / ETD_
  375.     dc.w    Clear-CmdTable        ;5    CMD_CLEAR / ETD_
  376.     dc.w    MyStop-CmdTable        ;6    CMD_STOP / ETD_
  377.     dc.w    Start-CmdTable        ;7    CMD_START
  378.     dc.w    Invalid-CmdTable    ;8    CMD_FLUSH
  379.     dc.w    Motor-CmdTable        ;9    TD_MOTOR / ETD_
  380.     dc.w    TDSeek-CmdTable        ;10    TD_SEEK / ETD_
  381.     dc.w    Format-CmdTable        ;11    TD_FORMAT
  382.     dc.w    TDRemove-CmdTable    ;12    TD_REMOVE
  383.     dc.w    ChangeNum-CmdTable    ;13    TD_CHANGENUM
  384.     dc.w    ChangeState-CmdTable    ;14    TD_CHANGESTATE
  385.     dc.w    ProtStatus-CmdTable    ;15    TD_PROTSTATUS
  386.     dc.w    TDRawRead-CmdTable    ;16    TD_RAWREAD
  387.     dc.w    RawWrite-CmdTable    ;17    TD_RAWWRITE
  388.     dc.w    TDGetDriveType-CmdTable    ;18    TD_GETDRIVETYPE
  389.     dc.w    GetNumTracks-CmdTable    ;19    TD_GETNUMTRACKS
  390.     dc.w    AddChangeInt-CmdTable    ;20    TD_ADDCHANGEINT
  391.     dc.w    RemChangeInt-CmdTable    ;21    TD_REMCHANGEINT
  392.     dc.w    GetGeometry-CmdTable    ;22    TD_GETGEOMETRY
  393.     dc.w    Invalid-CmdTable    ;23    TD_EJECT
  394. ENDCmdTable:
  395. HighestCommand    EQU    ((ENDCmdTable-CmdTable)/2)-1
  396. Invalid:    move.b    #IOERR_NOCMD,IO_ERROR(A_IO)
  397.     rts
  398.  
  399. InitRoutine:
  400. ;A0 - segment
  401. ;D0 - device ptr
  402.  
  403. ;Returns with the device ptr still in D0 if successful, otherwise zero.
  404.  
  405.     PUTDEBUG    <'InitRoutine: Entered'>
  406.     movem.l    d1-d7/a0-a6,-(sp)
  407.     move.l    d0,A_DEVICE
  408.     move.l    a0,SegList(A_DEVICE)
  409.     move.l    4,a6
  410.  
  411. ;Initialize ports
  412.     lea    TaskPort(A_DEVICE),a0
  413.     bsr    InitPort
  414.     lea    TimerPort(A_DEVICE),a0
  415.     bsr    InitPort
  416.  
  417. ;Invalidate track buffer
  418.     bsr    Clear
  419.  
  420. ;Open libraries/resources
  421.  
  422.     lea    DiskResourceName(pc),a1    ;'disk.resource'
  423.     SYS    OpenResource
  424.     move.l    d0,DiskResourceBase(A_DEVICE)
  425.     beq    .Failed
  426.  
  427. ;Try to allocate all the units.
  428.     move.l    d0,a6
  429.     moveq    #0,d2
  430. .AllocDisk:
  431.     move.l    d2,d0
  432.     IFND    _LVOAllocUnit
  433. _LVOAllocUnit    EQU    -6
  434.     ENDC
  435.     SYS    AllocUnit
  436.     addq.l    #1,d2
  437.     cmp.w    #4,d2
  438.     blo    .AllocDisk
  439.     move.l    4,a6
  440.  
  441.     lea    CIAName(pc),a1    ;'ciab.resource'
  442.     SYS    OpenResource
  443.     move.l    d0,CIABase(A_DEVICE)
  444.     beq    .Failed
  445.  
  446.     lea    GraphName(pc),a1
  447.     SYS    OldOpenLibrary
  448.     move.l    d0,GraphBase(A_DEVICE)
  449.     beq    .Failed
  450. ;hs    lea    IntName(pc),a1
  451. ;    SYS    OldOpenLibrary
  452. ;    move.l    d0,IntBase(A_DEVICE)
  453. ;    beq    .Failed
  454.  
  455. ;Allocate buffer memory.
  456.     move.l    #DISK_RawBufSize*2,d0
  457.     moveq    #MEMF_CHIP,d1
  458.     SYS    AllocMem
  459.     move.l    d0,RawBuffer(A_DEVICE)
  460.     beq    .Failed
  461.  
  462.     move.l    #512*SECPERTRACK*2,d0    22,d0    !??
  463.     moveq    #MEMF_CHIP,d1
  464.     SYS    AllocMem
  465.     move.l    d0,DecodedBuffer(A_DEVICE)
  466.     beq    .Failed
  467.  
  468.     move.l    #(1088*SECPERTRACK*2)+2,d0    !??
  469.     moveq    #MEMF_CHIP,d1
  470.     SYS    AllocMem
  471.     move.l    d0,VerifyBuffer(A_DEVICE)
  472.     beq    .Failed
  473.  
  474. ;Set up disk.resource structure.
  475.     lea    DiskResourcePort(A_DEVICE),a0
  476.     bsr    InitPort
  477.     move.l    a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
  478. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  479.     lea    Name(pc),a0
  480.     move.l    a0,DiskResourceUnit+LN_NAME(A_DEVICE)
  481.  
  482.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
  483.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
  484.     move.l    A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
  485.     lea    BlockInt(pc),a0
  486.     move.l    a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
  487.     lea    SyncInt(pc),a0
  488.     move.l    a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
  489.     lea    IndexInt(pc),a0
  490.     move.l    a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
  491.     moveq    #NT_INTERRUPT,d0
  492.     move.b    d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
  493.     move.b    d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
  494.     move.b    d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
  495.  
  496. ;Initialize timer IORequest (except signal)
  497.     lea    TimerPort(A_DEVICE),a0
  498. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  499.     move.l    a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
  500.     lea    TimerName(pc),a0
  501.     moveq    #UNIT_MICROHZ,d0
  502.     lea    TimerIORequest(A_DEVICE),a1
  503.     moveq    #0,d1
  504.     SYS    OpenDevice
  505.     tst.l    d0
  506.     bne    .Failed
  507.  
  508. ;*** Set up unit structures
  509.     moveq    #0,d2
  510.     lea    Unit0(A_DEVICE),A_UNIT
  511. .UnitLoop:
  512.     move.b    d2,UnitNum(A_UNIT)
  513.     lea    ChangeIntList(A_UNIT),a0
  514.     NEWLIST    a0
  515.     bset    #1,TDU_PUBFLAGS(A_UNIT)    ;verify ON!
  516.     move.b    #RETRYCNT,TDU_RETRYCNT(A_UNIT)
  517.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  518.     addq.b    #1,d2
  519.     cmp.b    #MD_NUMUNITS,d2
  520.     blo    .UnitLoop
  521.  
  522. ;Set up task
  523.     move.b    #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
  524.     move.b    #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
  525.     lea    Task(A_DEVICE),a1    ;Task Control Block
  526.  
  527. ;To make CrossDOS happy under 1.2/1.3
  528.     st    TC_Userdata(a1)
  529.  
  530.     lea    Name(pc),a0
  531.     move.l    a0,LN_NAME(a1)
  532.     move.b    #TASKPRI,LN_PRI(a1)
  533.     move.b    #NT_TASK,LN_TYPE(a1)
  534.     lea    TaskCode(pc),a2    ;initial PC
  535.     sub.l    a3,a3    ;final PC
  536.     lea    TaskStack(A_DEVICE),a0
  537.     move.l    a0,TC_SPLOWER(a1)
  538.     lea    STACKSIZE(a0),a0
  539.     move.l    a0,TC_SPUPPER(a1)
  540.  
  541.     move.l    A_DEVICE,-(a0)    ;pass device pointer to task
  542.     move.l    a0,TC_SPREG(a1)
  543.     move.l    a1,TaskPort+MP_SIGTASK(A_DEVICE)
  544.     SYS    AddTask
  545.  
  546.     move.l    A_DEVICE,d0
  547.     PUTDEBUG    <'InitRoutine: Done'>
  548. .End:    movem.l    (sp)+,d1-d7/a0-a6
  549.     rts
  550. .Failed:
  551.     PUTDEBUG    <'InitRoutine: Failed'>
  552. ;    bsr    CloseEverything
  553.     moveq    #0,d0
  554.     bra    .End
  555.  
  556. InitPort:
  557. ;Enter with pointer to port in A0. All registers preserved.
  558.     push    a0
  559.     lea    MP_MSGLIST(a0),a0
  560.     NEWLIST    a0
  561.     pop    a0
  562.     rts
  563.  
  564.     IFGT    0
  565. CloseEverything:
  566. ;This routines cleans up device stuff. Enter with A_DEVICE.
  567.  
  568.     movem.l    d0-d1/a0-a1/a6,-(sp)
  569.     move.l    4,a6
  570. ;Close timer
  571.     tst.b    TimerIORequest+IO_ERROR(A_DEVICE)
  572.     beq    .SkipTimer
  573.     lea    TimerIORequest(A_DEVICE),a1
  574.     SYS    CloseDevice
  575. .SkipTimer:
  576.  
  577. ;Remove task
  578.     lea    Name(pc),a1
  579.     SYS    FindTask
  580.     tst.l    d0
  581.     beq    .SkipTask
  582.     move.l    d0,a1
  583.     SYS    RemTask
  584. .SkipTask:
  585.  
  586. ;Close libraries
  587.     move.l    GraphBase(A_DEVICE),d0
  588.     beq    .SkipGfx
  589.     move.l    d0,a1
  590.     SYS    CloseLibrary
  591. .SkipGfx:
  592. ;hs    move.l    IntBase(A_DEVICE),d0
  593. ;    beq    .SkipInt
  594. ;    move.l    d0,a1
  595. ;    SYS    CloseLibrary
  596. ;.SkipInt:
  597.  
  598. ;Free CHIP RAM. (Note: This will change when I support other drive types).
  599.     move.l    RawBuffer(A_DEVICE),d0
  600.     beq    .SkipRaw
  601.     move.l    d0,a1
  602.     move.l    #DISK_RawBufSize,d0
  603.     SYS    FreeMem
  604. .SkipRaw:
  605.     move.l    DecodedBuffer(A_DEVICE),d0
  606.     beq    .SkipDecoded
  607.     move.l    d0,a1
  608.     move.l    #512*SECPERTRACK,d0
  609.     SYS    FreeMem
  610. .SkipDecoded:
  611.     move.l    VerifyBuffer(A_DEVICE),d0
  612.     beq    .SkipVerify
  613.     move.l    d0,a1
  614.     move.l    #(1088*SECPERTRACK)+2,d0
  615.     SYS    FreeMem
  616. .SkipVerify:
  617. .End:    movem.l    (sp)+,d0-d1/a0-a1/a6
  618.     rts
  619.  
  620.     ENDC
  621.  
  622.  
  623. ;******************************* Task code ******************************
  624.  
  625. TaskCode:
  626.     PUTDEBUG    <'Task: Entered'>
  627.     move.l    4,a6
  628.  
  629. ;Grab the arguments passed down from our parent
  630.     move.l    4(sp),A_DEVICE    ;Device pointer
  631.  
  632. ;General initialization
  633.  
  634.     lea    StartSigs(A_DEVICE),a2
  635.     moveq    #NumSigs-1,d2    ;Number of signals to allocate-1
  636. .SigLoop:
  637.     moveq    #-1,d0    ;-1 is any signal at all
  638.     SYS    AllocSignal    ;Allocate signals for I/O interrupts
  639.     moveq    #0,d1    ;Convert bit number signal mask
  640.     bset    d0,d1
  641.     move.l    d1,(a2)+    ;Save in unit structure
  642.     dbra    d2,.SigLoop
  643.  
  644.     moveq    #-1,d0    ;-1 is any signal at all
  645.     SYS    AllocSignal    ;Allocate a signal
  646.     move.b    d0,TaskPort+MP_SIGBIT(A_DEVICE)
  647.     move.b    #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE)    ;Make message port "live"
  648.  
  649.     moveq    #-1,d0    ;-1 is any signal at all
  650.     SYS    AllocSignal    ;Allocate a signal
  651.     move.b    d0,TimerPort+MP_SIGBIT(A_DEVICE)
  652.     move.l    ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
  653.  
  654.     moveq    #-1,d0    ;-1 is any signal at all
  655.     SYS    AllocSignal    ;Allocate a signal
  656.     move.b    d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
  657.     move.l    ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
  658.  
  659.     bsr    GetDrive
  660.     bsr    Inquire
  661.  
  662. ;Get drive types
  663.     moveq    #MD_NUMUNITS-1,d0
  664.     lea    Unit0(A_DEVICE),A_UNIT
  665. .TypeLoop:
  666.     bsr    GetDriveType
  667.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  668.     dbra    d0,.TypeLoop
  669.  
  670.     bsr    SeekZeroAll    ;Send all drives to track zero.
  671.     bsr    FreeDrive
  672.  
  673.     bra    .NextMessage
  674.  
  675. ;Main loop: Wait for a new message and handle disk changes.
  676.  
  677. .MainLoop:
  678.     moveq    #0,d0
  679.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  680.     bset    d1,d0
  681.     move.l    #500000,d1
  682.     bsr    TimeOutWait
  683.     tst.l    d0
  684.     bne    .NextMessage
  685.  
  686. ;0.5 seconds have passed without receiving any work, so we check for disk
  687. ;changes, then drop into .NextMessage.
  688. ;Note: A6 undefined.
  689.  
  690. .CheckDiskChange:
  691.     moveq    #MD_NUMUNITS-1,d2
  692.     move.b    InquireBits(A_DEVICE),d3
  693.     lea    Unit0(A_DEVICE),A_UNIT
  694.     bsr    GetDrive
  695.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  696.  
  697. ;Algorithm (for each unit):
  698. ;Check hardware diskchange bit. If disk was ejected, seek to track zero,
  699. ;clear DiskInDrive bit. End.
  700. ;If no disk in drive, check NoClick bit. If set, immediately step toward
  701. ;track zero (using custom step routine). If clear, check the
  702. ;Drive_LastChecked time against the current time. If less than 2.5 seconds,
  703. ;End. If >2.5 seconds, step the head toward track zero, unless already on
  704. ;track zero. Check the hardware diskchange bit. If a disk is present in the
  705. ;drive set the DiskInDrive bit and check the hardware write protect status,
  706. ;setting WriteProtected as needed. End.
  707. ;Note: We also increment the diskchange counter in the public part of the
  708. ;unit structure, if a disk was inserted or removed.
  709.  
  710. .UnitLoop:
  711.     move.b    UnitNum(A_UNIT),d4
  712.     btst    d4,d3
  713.     beq    .NoCheck
  714.  
  715.     lea    TempTimeVal(A_DEVICE),a0
  716.     bsr    GetSysTime
  717.  
  718.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  719.     beq    .ThinkNoDisk
  720.     bsr    SelectDriveSameMotor
  721.     btst    #CIAB_DSKCHANGE,ciaapra
  722.     bne    .NoChange
  723. ;Disk was removed.
  724.     bclr    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  725.     bsr    SeekZero
  726.     bsr    MotorOff
  727.     bsr    Clear
  728.     bra    .Change
  729. .ThinkNoDisk:
  730.     btst    #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
  731.     beq    .Click
  732.     bsr    SelectDriveSameMotor
  733.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  734.     bset    #CIAB_DSKSTEP,ciabprb
  735.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  736.     bset    #CIAB_DSKSTEP,ciabprb
  737.     bsr    Deselect
  738.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  739.     bsr    delay
  740.     bsr    SelectDriveSameMotor
  741.     bra    .ChkChg
  742. .Click:
  743.     lea    Drive_LastCheck(A_UNIT),a1
  744.     SYS    SubTime
  745.     cmp.l    #2,TV_SECS(a0)
  746.     blo    .NoCheck
  747.     bhi    .SkipMicro
  748.     cmp.l    #500000,TV_MICRO(a0)
  749.     blo    .NoCheck
  750. .SkipMicro:
  751.     lea    Drive_LastCheck(A_UNIT),a0
  752.     bsr    GetSysTime
  753.     move.b    UnitNum(A_UNIT),d2    ;for Seek
  754.     move.w    TDU_CURRTRK(A_UNIT),d3
  755.     subq.w    #2,d3
  756.     bpl    .NotZero
  757.     moveq    #2,d3
  758. .NotZero:    bsr    SelectDriveSameMotor
  759.     bsr    SeekNoUpdate
  760. .ChkChg:    btst    #CIAB_DSKCHANGE,ciaapra
  761.     beq    .NoChange    ;no disk in drive
  762.     bset    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  763.     bclr    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  764.     btst    #CIAB_DSKPROT,ciaapra
  765.     bne    .Change    ;not write protected
  766.     bset    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  767. .Change:    bsr    GetDriveType    ;reread drive type (leaves drive deselected)
  768. ;    bsr    Deselect
  769.     addq.l    #1,TDU_COUNTER(A_UNIT)
  770.  
  771. ;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
  772.  
  773.     push    a6
  774.     move.l    4,a6
  775.     SYS    Forbid
  776.     move.l    TDRemoveInt(A_UNIT),d0
  777.     beq    .NoRemoveInt
  778.     move.l    d0,a1
  779.     SYS    Cause
  780. .NoRemoveInt:
  781.     move.l    ChangeIntList+LH_HEAD(A_UNIT),a2
  782. .IntLoop:    move.l    (a2),d0
  783.     beq    .DoneInt
  784.     move.l    IO_DATA(a2),a1
  785.     move.l    d0,a2
  786.     SYS    Cause
  787.     bra    .IntLoop
  788. .DoneInt:    SYS    Permit
  789.     pop    a6
  790. .NoChange:
  791.     lea    LastCheck(A_DEVICE),a0
  792.     bsr    GetSysTime
  793. .NoCheck:
  794.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  795.     dbra    d2,.UnitLoop
  796.     move.l    4,a6
  797.     bsr    FreeDrive
  798.  
  799. .NextMessage:
  800.     btst    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)    ;See if we are stopped
  801.     bne    .MainLoop    ;device is stopped, so ignore messages
  802.  
  803.     lea    TaskPort(A_DEVICE),a0
  804.     SYS    GetMsg    ;Get the next request
  805.     tst.l    d0
  806.     beq    .MainLoop    ;no message?
  807.     move.l    d0,A_IO    ;Do this request
  808.     move.l    IO_UNIT(A_IO),A_UNIT
  809.  
  810. ;Handle TDB_EXTCOM and dispatch command
  811.     move.w    IO_COMMAND(A_IO),d0
  812.  
  813.     IFNE    INFO_LEVEL
  814.     swap    d0
  815.     clr.w    d0
  816.     swap    d0
  817.     move.l    d0,-(sp)
  818.     PUTDEBUG    <'Command=%ld'>
  819.     addq.l    #4,sp
  820.     ENDC
  821.  
  822.     bclr    #TDB_EXTCOM,d0
  823.     beq    .NoExt
  824.     move.l    TDU_COUNTER(A_UNIT),d1
  825.     cmp.l    IOTD_COUNT(A_IO),d1
  826.     bls    .NoExt
  827. .ChgErr:    PUTDEBUG    <'Change error!'>
  828.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  829.     bra    .Reply
  830. .NoExt:    move.l    #%000000111000111000011100,d1    ;specifies which commands should check for disk
  831.     btst    d0,d1
  832.     beq    .NoDiskNeeded
  833.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  834.     beq    .ChgErr
  835. .NoDiskNeeded:
  836.     bsr    GetDrive
  837.     lea    CmdTable(pc),a0
  838.     add.w    d0,d0
  839.     add.w    (a0,d0.w),a0
  840.     moveq    #0,d7    ;clear error flag
  841.     jsr    (a0)
  842.     bsr    FreeDrive
  843.     cmp.w    #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
  844.     beq    .SkipReply
  845. .Reply:    move.l    A_IO,a1
  846.     SYS    ReplyMsg
  847. .SkipReply:
  848.  
  849. ;If at least 0.5 seconds has passed since last checking diskchange, do so
  850. ;now.
  851.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  852.     lea    TempTimeVal(A_DEVICE),a0
  853.     bsr    GetSysTime
  854.     lea    LastCheck(A_DEVICE),a1
  855.     SYS    SubTime
  856.     tst.l    TV_SECS(a0)
  857.     bne    .CheckDiskChange
  858.     cmp.l    #500000,TV_MICRO(a0)
  859.     bhs    .CheckDiskChange
  860.     lea    LastCheck(A_DEVICE),a0
  861.     bsr    GetSysTime
  862.     move.l    4,a6
  863.     bra    .NextMessage
  864.  
  865. GetSysTime:
  866.     cmp.w    #36,LIB_VERSION(a6)
  867.     bls    .OldKS
  868.     jmp    _LVOGetSysTime(a6)
  869. .OldKS:
  870. ;Compatibility routine for 1.2/1.3
  871.     movem.l    d0-d1/a0-a2/a6,-(sp)
  872.     move.l    a0,a2
  873.     lea    TimerIORequest(A_DEVICE),a1
  874.     move.l    4,a6
  875.     move.w    #TR_GETSYSTIME,IO_COMMAND(a1)
  876.     SYS    DoIO
  877.     lea    TimerIORequest(A_DEVICE),a1
  878.     move.l    IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
  879.     move.l    IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
  880.     movem.l    (sp)+,d0-d1/a0-a2/a6
  881.     rts
  882.  
  883. ;******************** External device routines ***********************
  884.  
  885. Open:
  886.  
  887. ;A6 - device ptr
  888. ;A1 - IORequest
  889. ;D0 - unit number
  890. ;D1 - flags (not used)
  891.  
  892. ;Important: On error, MUST put -1 in IO_DEVICE. This routine has no return
  893. ;value in d0 (return code is in IO_ERROR).
  894.  
  895.     PUTDEBUG    <'Open: Entered'>
  896.     moveq    #MD_NUMUNITS,d1
  897.     cmp.l    d1,d0
  898.     bhs    .Error
  899.     btst    d0,InquireBits(a6)
  900.     beq    .Error
  901.     lea    Unit0(a6),a0
  902.     mulu.w    #MyUnit_Sizeof,d0
  903.     add.l    d0,a0
  904.     move.l    a0,IO_UNIT(a1)
  905.     addq.w    #1,LIB_OPENCNT(a6)
  906.     clr.b    IO_ERROR(a1)    ;no error
  907.     move.b    #NT_REPLYMSG,LN_TYPE(a1)    ;Mark IORequest as "complete"
  908.     PUTDEBUG    <'Open: Done'>
  909.     rts
  910. .Error:
  911.     PUTDEBUG    <'Open: Failed'>
  912.  
  913. ;VirusX didn't like this.
  914. ;    move.b    #IOERR_OPENFAIL,IO_ERROR(a1)
  915.  
  916. ;NOTE: Trackdisk will return TDERR_BadDriveType if TDB_ALLOW_NON_3_5 flag set
  917. ;in "flags" and the drive is not present. (This is just trivia).
  918.  
  919.     move.b    #TDERR_BadUnitNum,IO_ERROR(a1)
  920.     moveq    #-1,d0
  921.     move.l    d0,IO_DEVICE(a1)
  922.     rts
  923.  
  924. _Close:
  925.  
  926. ;A6 - device ptr
  927. ;A1 - IORequest
  928.  
  929. ;Must return either 0 or, if the device wishes to be unloaded, the segment
  930. ;list.
  931.  
  932.     PUTDEBUG    <'Close: Called'>
  933.     moveq    #0,d0
  934.     rts
  935.  
  936. Expunge:
  937.  
  938. ;A6 - device ptr
  939.  
  940. ;Must return either 0 or SegList ptr in D0.
  941.  
  942.     moveq    #0,d0
  943.     rts
  944.  
  945. BeginIO:
  946. ;A1 - IORequest
  947. ;A6 - device ptr
  948.  
  949.     movem.l    d7/a3-a6,-(sp)
  950.     move.l    a1,A_IO
  951.     move.l    IO_UNIT(A_IO),A_UNIT
  952.     move.l    a6,A_DEVICE
  953.     move.l    4,a6
  954.  
  955.     clr.b    IO_ERROR(A_IO)
  956.     move.b    #NT_MESSAGE,LN_TYPE(A_IO)    ;So WaitIO() is guaranteed to work
  957.  
  958. ;Decide whether the command is immediate or queued
  959.     move.w    IO_COMMAND(A_IO),d1
  960.     bclr    #15,d1
  961.     cmp.w    #HighestCommand,d1
  962.     bhi    .BadCmd
  963.     move.l    #%111011001111000110000011,d0    ;specifies what commands are immediate
  964.     btst    d1,d0
  965.     beq    .Queue
  966.     add.w    d1,d1
  967.     lea    CmdTable(pc),a0
  968.     add.w    (a0,d1.w),a0
  969.     moveq    #0,d7    ;clear error flag
  970.     jsr    (a0)
  971. .Reply:    btst    #IOB_QUICK,IO_FLAGS(A_IO)
  972.     bne    .End
  973.     move.l    A_IO,a1
  974.     SYS    ReplyMsg
  975. .End:    movem.l    (sp)+,d7/a3-a6
  976.     rts
  977. .Queue:    bclr    #IOB_QUICK,IO_FLAGS(A_IO)    ;We did NOT complete this quickly
  978.     lea    TaskPort(A_DEVICE),a0
  979.     move.l    A_IO,a1
  980.     SYS    PutMsg
  981.     bra    .End
  982. .BadCmd:    move.b    #IOERR_NOCMD,IO_ERROR(a1)
  983.     bra    .Reply
  984.  
  985. AbortIO:
  986.  
  987. ;A6 - device ptr
  988. ;A1 - IORequest
  989.  
  990.     move.b    #IOERR_ABORTED,IO_ERROR(a1)    ;We always say we succeed(ed)
  991.     moveq    #0,d0    ;another success code
  992.     rts
  993.  
  994. ;**************** Device command (IO_COMMAND) routines *************************
  995.  
  996. ;Note: A6 = ExecBase upon entering a command routine.
  997. ;The low-level routines load $DFF000 into A6 when needed.
  998.  
  999.     IFND    DEVPAC
  1000. SaveRegs    setrl    d0-d3/d6-d7/a0-a2/a6
  1001.     ENDC
  1002. Read:
  1003.     PUTDEBUG    <'Read: Called'>
  1004.     IFD    DEVPAC
  1005.     movem.l    d0-d3/d6-d7/a0-a2/a6,-(sp)
  1006.     ELSE
  1007.     movem.l    SaveRegs,-(sp)
  1008.     ENDC
  1009.     bsr    RWSetup
  1010.     tst.l    d7
  1011.     bne    FinishRW
  1012.  
  1013.     bsr    DISK_Update    ;required because of complex write scheme
  1014. ;Check for sector label nonsense
  1015.     tst.b    IO_COMMAND(A_IO)
  1016.     bpl    .NoLabel
  1017.     move.l    IOTD_SECLABEL(A_IO),d2
  1018.     bne    ReadSecLabel
  1019. .NoLabel:
  1020.  
  1021.     bsr    DISK_Read
  1022.     PUTDEBUG    <'Read: Done'>
  1023.     bra    FinishRW
  1024. Format:
  1025. Write:
  1026.     IFD    DEVPAC
  1027.     movem.l    d0-d3/d6-d7/a0-a2/a6,-(sp)
  1028.     ELSE
  1029.     movem.l    SaveRegs,-(sp)
  1030.     ENDC
  1031.     bsr    RWSetup
  1032.     tst.l    d7
  1033.     bne    FinishRW
  1034.  
  1035. ;Check for sector label nonsense
  1036.     tst.b    IO_COMMAND(A_IO)
  1037.     bpl    .NoLabel
  1038.     move.l    IOTD_SECLABEL(A_IO),d2
  1039.     bne    WriteSecLabel
  1040. .NoLabel:
  1041.  
  1042.     bsr    DISK_Write
  1043. FinishRW:    clr.l    IO_ACTUAL(A_IO)
  1044.     move.b    d7,IO_ERROR(A_IO)
  1045.     bne    .Skip
  1046.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1047. .Skip:
  1048.     IFD    DEVPAC
  1049.     movem.l    (sp)+,d0-d3/d6-d7/a0-a2/a6
  1050.     ELSE
  1051.     movem.l    (sp)+,SaveRegs
  1052.     ENDC
  1053.     rts
  1054. RWSetup:
  1055.     move.l    SectorsPerTrack(A_UNIT),d6
  1056.     lsl.l    #8,d6
  1057.     add.l    d6,d6    ;*512
  1058.     move.l    #_custom,a6
  1059.     move.l    IO_LENGTH(A_IO),d0
  1060.     move.l    IO_OFFSET(A_IO),d1
  1061.     move.l    IO_DATA(A_IO),a0
  1062.     move.l    d1,d2    ;offset
  1063.     add.l    d0,d2    ;length
  1064.     cmp.l    BytesPerDisk(A_UNIT),d2
  1065.     bhi    .Error
  1066.     bsr    SelectDrive
  1067.     bra    SelectSide
  1068. .Error:    moveq    #DISK_BadParameter,d7
  1069.     rts
  1070.  
  1071. ;Here are the routines for dealing with read/write requests that involve
  1072. ;the sector label. Note that, unlike the usual read/write routines of my
  1073. ;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
  1074. ;must be a multiple of 512) MUST be observed. And I don't check for illegal
  1075. ;parameters, either. These routines are optimized for compactness rather
  1076. ;than performance because they are rarely (if ever) used.
  1077. ;Enter with:
  1078. ;IO_LENGTH: D0
  1079. ;IO_OFFSET: D1
  1080. ;IO_DATA:   A0
  1081.  
  1082. ReadSecLabel:
  1083.     PUTDEBUG    <'READSECLABEL!'>
  1084.  
  1085.     move.l    d2,a1
  1086.     move.l    d0,d2
  1087.     move.l    #512,d0
  1088. .Read:    bsr    DISK_Read
  1089.     move.l    d1,d3
  1090.     add.l    d0,d1
  1091.     add.l    d0,a0
  1092.     divu.w    d6,d3    ;offset/tracksize
  1093.     swap    d3    ;remainder=sector# (in bytes)
  1094.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1095.     lea    SectorLabels(A_DEVICE),a2
  1096.     lea    (a2,d3.w),a2    ;get pointer to label
  1097.     moveq    #3,d3
  1098. ..    move.l    (a2)+,(a1)+    ;copy label
  1099.     dbra    d3,..
  1100.     sub.l    d0,d2
  1101.     bne    .Read
  1102.     bra    FinishRW
  1103.  
  1104. WriteSecLabel:
  1105.     PUTDEBUG    <'WRITESECLABEL!'>
  1106.  
  1107.     move.l    d2,a1
  1108.     move.l    d0,d2
  1109. .Write:
  1110.     move.l    d1,d3
  1111.     divu.w    d6,d3    ;offset/tracksize
  1112.     swap    d3    ;remainder=sector# (in bytes)
  1113.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1114.  
  1115. ;We check to see whether we can write a full track. (Somehow, I can't
  1116. ;ignore performance completely :-)).
  1117.     tst.w    d3
  1118.     bne    .Read
  1119.     cmp.l    d6,d0
  1120.     bhs    .FullTrack
  1121. .Read:    moveq    #0,d0
  1122.     bsr    DISK_Read    ;force a track read
  1123.     lea    SectorLabels(A_DEVICE),a2
  1124.     lea    (a2,d3.w),a2    ;get pointer to label
  1125.     moveq    #3,d3
  1126. ..    move.l    (a1)+,(a2)+    ;copy label
  1127.     dbra    d3,..
  1128.     move.l    #512,d0
  1129. .L1:    bsr    DISK_Write
  1130.     add.l    d0,d1
  1131.     add.l    d0,a0
  1132.     sub.l    d0,d2
  1133.     bne    .Write
  1134.     bra    FinishRW
  1135. .FullTrack:
  1136.     lea    SectorLabels(A_DEVICE),a2
  1137.     moveq    #((16*SECPERTRACK*2)/4)-1,d3    22 !??
  1138. .il    move.l    (a1)+,(a2)+    ;copy all sector labels
  1139.     dbra    d3,.il
  1140.     move.l    d6,d0
  1141.     bra    .L1
  1142.  
  1143. Update:    move.l    d7,-(sp)
  1144.     moveq    #0,d7
  1145.     bsr    DISK_Update
  1146.     move.b    d7,IO_ERROR(A_IO)
  1147.     move.l    (sp)+,d7
  1148.     rts
  1149.  
  1150. Clear:
  1151. ;This routine marks the track buffer as invalid.
  1152.     st    BufferDrive(A_DEVICE)
  1153.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1154.     rts
  1155.  
  1156. Motor:
  1157. ;Controls the drive motor
  1158. ;If IO_LENGTH=1, motor on
  1159. ;   IO_LENGTH=0, motor off
  1160. ;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
  1161.  
  1162.     push    d2
  1163.     clr.l    IO_ACTUAL(A_IO)
  1164.     move.b    UnitNum(A_UNIT),d2
  1165.     btst    d2,MotorState(A_DEVICE)
  1166.     beq    .WasOff
  1167.     move.l    #1,IO_ACTUAL(A_IO)
  1168. .WasOff:    tst.l    IO_LENGTH(A_IO)
  1169.     beq    .Off
  1170. ;Turn motor on
  1171.     bsr    SelectDrive
  1172.     bra    .End
  1173. .Off:
  1174. ;Turn motor off
  1175.     bclr    d2,MotorState(A_DEVICE)
  1176.     bsr    Deselect
  1177.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1178.     addq.b    #3,d2
  1179.     bclr    d2,ciabprb    ;select drive X
  1180. .End:    pop    d2
  1181.     rts
  1182.  
  1183. ChangeState:
  1184. ;Returns whether there is a disk currently in the drive
  1185. ;IO_ACTUAL=0 if disk in drive
  1186. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1187.  
  1188.     clr.l    IO_ACTUAL(A_IO)
  1189.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1190.     bne    .End
  1191.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1192. .End:    rts
  1193.  
  1194. ChangeNum:
  1195. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1196.     move.l    TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
  1197.     rts
  1198.  
  1199. ProtStatus:
  1200. ;IO_ACTUAL=0 if disk is not write protected
  1201. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1202.  
  1203. ;Very important for compatibility: we must return an error (disk changed)
  1204. ;if there's no disk in the drive.
  1205.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1206.     bne    .OK
  1207.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  1208.     rts
  1209.  
  1210. .OK:    clr.l    IO_ACTUAL(A_IO)
  1211.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1212.     beq    .End    ;not write protected
  1213.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1214. .End:    rts
  1215.  
  1216. TDGetDriveType:
  1217. ;Returns type of drive in IO_ACTUAL
  1218. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1219.     clr.l    IO_ACTUAL(A_IO)
  1220.     move.b    DriveType(A_UNIT),IO_ACTUAL+3(A_IO)
  1221.     rts
  1222.  
  1223. GetNumTracks:
  1224. ;Returns number of tracks (note: not cylinders) in IO_ACTUAL
  1225. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1226.  
  1227.     clr.l    IO_ACTUAL(A_IO)
  1228.     move.b    NumTracks(A_UNIT),IO_ACTUAL+3(A_IO)
  1229.     rts
  1230.  
  1231. GetGeometry:
  1232. ;Fills in DriveGeometry structure pointed to by IO_DATA
  1233.     movem.l    d0-d1/a0,-(sp)
  1234.     move.l    IO_DATA(A_IO),a0
  1235.     move.l    #512,dg_SectorSize(a0)    ;in bytes
  1236.     move.l    #2,dg_Heads(a0)    ;number of surfaces
  1237.     clr.l    dg_BufMemType(a0)    ;preferred buffer memory type
  1238.     clr.b    dg_DeviceType(a0)    ;codes as defined in the SCSI-2 spec
  1239.     move.b    #DGF_REMOVABLE,dg_Flags(a0)    ;flags, including removable
  1240.     clr.w    dg_Reserved(a0)
  1241.     move.l    SectorsPerTrack(A_UNIT),d0
  1242.     move.l    d0,dg_TrackSectors(a0)    ;number of sectors/track
  1243.     add.l    d0,d0
  1244.     move.l    d0,dg_CylSectors(a0)    ;number of sectors/cylinder
  1245.     moveq    #0,d1
  1246.     move.b    NumTracks(A_UNIT),d1
  1247.     lsr.l    #1,d1    ;convert tracks -> cylinders
  1248.     move.l    d1,dg_Cylinders(a0)    ;number of cylinders
  1249.     mulu.w    d0,d1
  1250.     move.l    d1,dg_TotalSectors(a0)    ;total # of sectors on drive
  1251.     movem.l    (sp)+,d0-d1/a0
  1252.     rts
  1253.  
  1254. MyStop:    bset    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1255.     bsr    Update
  1256.     bra    Clear
  1257. Start:    movem.l    d0-d1/a0-a1,-(sp)
  1258.     bclr    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1259.     moveq    #0,d0
  1260.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  1261.     bset    d1,d0
  1262.     lea    Task(A_DEVICE),a1
  1263.     SYS    Signal
  1264.     movem.l    (sp)+,d0-d1/a0-a1
  1265.     rts
  1266.  
  1267. ;Note: This seek routine can't really be depended on by user programs,
  1268. ;because the disk.resource will corrupt the side select bit.
  1269. TDSeek:    movem.l    d0/d3,-(sp)
  1270.     bsr    SelectDriveSameMotor
  1271.     move.l    IO_OFFSET(A_IO),d3
  1272.     cmp.l    BytesPerDisk(A_UNIT),d3
  1273.     bhs    .Error
  1274.     move.l    SectorsPerTrack(A_UNIT),d0
  1275.     lsl.l    #8,d0
  1276.     add.l    d0,d0    ;*512 = bytes per track
  1277.     divu.w    d0,d3
  1278.     bsr    Seek
  1279. .End:    movem.l    (sp)+,d0/d3
  1280.     rts
  1281. .Error:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1282.     bra    .End
  1283.  
  1284. TDRawRead:
  1285.     movem.l    d0-d1/d3/a6,-(sp)
  1286.     move.l    #_custom,a6
  1287.     moveq    #0,d1
  1288.     bsr    DoRaw
  1289.     movem.l    (sp)+,d0-d1/d3/a6
  1290.     rts
  1291.  
  1292. RawWrite:
  1293.     movem.l    d0-d1/d3/a6,-(sp)
  1294.     move.l    #_custom,a6
  1295.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1296.     bne    .WPErr
  1297.     move.w    #1<<14,d1
  1298.     bsr    DoRaw
  1299.     move.l    #3*1000,d0
  1300.     bsr    delay    ;post-write delay
  1301. .End:    movem.l    (sp)+,d0-d1/d3/a6
  1302.     rts
  1303. .WPErr:    move.b    #TDERR_WriteProt,IO_ERROR(A_IO)
  1304.     bra    .End
  1305.  
  1306. DoRaw:
  1307. ;Enter with bit 14 set in d1 according to read/write (for dsklen)
  1308. ;Uses d0-d1/d3 (not saved)
  1309. ;Assumes $dff000 in A6
  1310.  
  1311.     clr.l    IO_ACTUAL(A_IO)
  1312.     bsr    SelectDrive
  1313.     move.l    IO_OFFSET(A_IO),d3
  1314.     cmp.b    NumTracks(A_UNIT),d3
  1315.     bhs    .Error
  1316.     bsr    Seek
  1317.     tst.l    d7
  1318.     bne    .Error
  1319.  
  1320.     move.l    IO_DATA(A_IO),dskpt(a6)
  1321.     move.w    #ADKF_WORDSYNC,adkcon(a6)
  1322.     btst    #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
  1323.     beq    .SkipSync
  1324.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1325. .SkipSync:
  1326.     move.w    #ADKF_MSBSYNC,adkcon(a6)
  1327.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
  1328.     bsr    PreComp
  1329.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1330.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1331.     bsr    ClearSigs
  1332.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable int
  1333.     move.l    IO_LENGTH(A_IO),d0
  1334.  
  1335.     cmp.l    #32766,d0
  1336.     bhi    .Error
  1337.     lsr.l    #1,d0
  1338.     bset    #15,d0
  1339.     or.w    d1,d0
  1340.  
  1341.     btst    #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
  1342.     beq    .NoIndex
  1343.     move.w    d0,IndexDskLen(A_DEVICE)
  1344.     bsr    EnableIndex
  1345.     bra    .Wait
  1346. .NoIndex:
  1347.  
  1348.     move.w    d0,dsklen(a6)
  1349.     move.w    d0,dsklen(a6)
  1350. .Wait:    move.l    BlockSig(A_DEVICE),d0
  1351.     move.l    #900*1000*2,d1
  1352.     bsr    TimeOutWait
  1353.     beq    .NoSync
  1354.     move.w    #0,dsklen(a6)
  1355.     move.w    #INTF_DSKBLK,intena(a6)    ;disable int
  1356. .EndIO:    bsr    DisableIndex
  1357.     bsr    ClearSigs
  1358.     tst.b    IO_ERROR(A_IO)
  1359.     bne    .EndRTS
  1360.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1361. .EndRTS:    rts
  1362. .Error:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1363.     rts
  1364. .NoSync:    bsr    StopDMA
  1365.     move.b    #TDERR_NoSecHdr,IO_ERROR(A_IO)
  1366.     bra    .EndIO
  1367.  
  1368.  
  1369. AddChangeInt:
  1370. ;This command uses the linkage fields of the IORequest to add a ChangeInt
  1371. ;request to a list maintained in the unit structure. This must NOT be
  1372. ;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
  1373. ;the task code handles this.
  1374.     movem.l    d0-d1/a0-a1,-(sp)
  1375.     move.l    A_IO,a1
  1376.     lea    ChangeIntList(A_UNIT),a0
  1377.     SYS    AddHead    ;could use any list add routine
  1378.     movem.l    (sp)+,d0-d1/a0-a1
  1379.     rts
  1380.  
  1381. RemChangeInt:
  1382. ;This command unlinks the AddChangeInt request from the task's port.
  1383. ;This is (and must be) an immediate command.
  1384. ;Note that because this command occurs asyncronously to the task code, the
  1385. ;task code that scans the ChangeInt list must be protected with a Forbid.
  1386.  
  1387.     movem.l    d0-d1/a0-a1,-(sp)
  1388.     SYS    Forbid
  1389.     move.l    A_IO,a1
  1390.     SYS    Remove
  1391.     SYS    Permit
  1392.     movem.l    (sp)+,d0-d1/a0-a1
  1393.     rts
  1394.  
  1395. TDRemove:
  1396. ;This command is obsolete, but is unfortuntely used by the ROM filesystem.
  1397. ;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
  1398. ;unit is permitted.
  1399.  
  1400.     tst.l    TDRemoveInt(A_UNIT)
  1401.     beq    .Ok
  1402.     move.b    #TDERR_DriveInUse,IO_ERROR(A_IO)
  1403.     rts
  1404. .Ok:    move.l    IO_DATA(A_IO),TDRemoveInt(A_UNIT)
  1405.     rts
  1406.  
  1407. GetDrive:
  1408. ;Obtain the use of this unit via disk.resource.
  1409.     movem.l    d0-d1/a0-a2/a6,-(sp)
  1410.     lea    DiskResourceUnit(A_DEVICE),a2
  1411. .GetUnit:    move.l    DiskResourceBase(A_DEVICE),a6
  1412.     move.l    a2,a1
  1413.     jsr    DR_GETUNIT(a6)
  1414.     tst.l    d0
  1415.     bne    .End
  1416.     lea    DiskResourcePort(A_DEVICE),a0
  1417.     move.l    4,a6
  1418.     SYS    WaitPort
  1419.     bra    .GetUnit
  1420. .End:    movem.l    (sp)+,d0-d1/a0-a2/a6
  1421.     rts
  1422.  
  1423. FreeDrive:
  1424. ;Release this unit to other users of disk.resource.
  1425.  
  1426.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1427.     move.l    DiskResourceBase(A_DEVICE),a6
  1428.     jsr    DR_GIVEUNIT(a6)
  1429.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1430.     rts
  1431.  
  1432. ;***************************** Low-level disk code **************************
  1433.  
  1434. ;Standard register usage:
  1435. ;A6            - $dff000
  1436. ;A5 (A_DEVICE) - device ptr
  1437. ;A4 (A_UNIT)   - unit ptr
  1438. ;A3 (A_IO)     - pointer to IO request
  1439.  
  1440. ;For reading/writing:
  1441. ;D0.L - length (bytes)
  1442. ;D1.L - offset (bytes)
  1443. ;A0.L - buffer
  1444.  
  1445. ;NOTE: Error code returned in D7.L - 0 if ok, else error
  1446.  
  1447. ;General errors
  1448. DISK_BadParameter    EQU    TDERR_NotSpecified
  1449.  
  1450. ;Read errors
  1451. DISK_NoSync    EQU    TDERR_NoSecHdr
  1452. DISK_BadHeader    EQU    TDERR_BadHdrSum
  1453. DISK_BadData    EQU    TDERR_BadSecSum
  1454.  
  1455. ;Write errors
  1456. DISK_WriteProtected    EQU    TDERR_WriteProt
  1457. DISK_VerifyError    EQU    TDERR_NotSpecified
  1458.  
  1459.     IFND    dskpt
  1460. dskpt    EQU    dskpth
  1461.     ENDC
  1462.  
  1463. ;Note: This is a public value, but it is NOT meant to be changed!!!
  1464. DISK_RawBufSize    EQU    13630
  1465.  
  1466. ciabprb    EQU    $bfd100
  1467. ciaapra    EQU    $bfe001
  1468. ciabddrb    EQU    $bfd300
  1469. ciaaddra    EQU    $bfe201
  1470.  
  1471. ***************************************************************
  1472. ;Important disk parameters, summary:
  1473.  
  1474. ;Step rate: 3ms. 4ms when looking for track zero.
  1475. ;Settle time: 15ms
  1476. ;Post-write delay: 3ms (officially 1.3ms)
  1477. ;Side select delay: 2ms (officially 1.3ms)
  1478. ***************************************************************
  1479.  
  1480. DISK_Read:
  1481.     movem.l    d0-d5/a0/a1,-(sp)
  1482.  
  1483.     move.l    a0,a1    ;destination ptr in A1
  1484.     move.l    d0,d5
  1485. .ReadLoop:
  1486.     move.l    d1,d3
  1487.     divu.w    d6,d3    ;d3.w is track #
  1488.     move.l    d3,d4
  1489.     clr.w    d4
  1490.     swap    d4    ;d4.l is byte offset into track
  1491.     bsr    MinSeek
  1492.     tst.l    d4    ;any offset?
  1493.     bne    .Complex    ;yes
  1494.     cmp.l    d6,d5    ;check remaining length
  1495.     blo    .Complex    ;if not a track, forget it
  1496.     move.l    a1,d0
  1497.     btst    #0,d0    ;word aligned?
  1498.     bne    .Complex    ;no, do it the long way
  1499.     move.l    a1,a0
  1500.     bsr    ReadTrackAndDecodeNoBuffer
  1501.     tst.l    d7
  1502.     bne    .End
  1503.     sub.l    d6,d5    ;update length
  1504.     beq    .End
  1505.     add.l    d6,a1    ;update destination pointer
  1506.     add.l    d6,d1    ;update offset
  1507.     bra    .ReadLoop
  1508.  
  1509. .Complex:
  1510.     move.l    DecodedBuffer(A_DEVICE),a0
  1511.     bsr    ReadTrackAndDecodeBuffered
  1512.     tst.l    d7
  1513.     bne    .End
  1514.     add.l    d4,a0
  1515.     sub.l    d6,d4
  1516.     neg.l    d4
  1517.     add.l    d4,d1    ;update offset
  1518.  
  1519. ;D4.L - number of bytes that could be transferred from this track
  1520. ;D5.L - number of bytes left in the entire Read request
  1521.  
  1522.     cmp.l    d5,d4
  1523.     bhs    .FinishUp
  1524.     sub.l    d4,d5    ;update length
  1525.     move.l    d4,d0
  1526.     bsr    CopyMem
  1527.     add.l    d0,a1    ;update destination pointer
  1528.     bra    .ReadLoop
  1529. .FinishUp:
  1530.     move.l    d5,d0
  1531.     bsr    CopyMem    ;use number of bytes left in entire request
  1532. .End:    movem.l    (sp)+,d0-d5/a0/a1
  1533.     rts
  1534.  
  1535. SelectDrive:
  1536. ;Selects drive AND turns on motor
  1537. ;Selects UnitNum(A_UNIT).
  1538.  
  1539.     movem.l    d0/d2,-(sp)
  1540.     move.b    UnitNum(A_UNIT),d2
  1541.     bsr    Deselect
  1542.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1543.     move.l    d2,d0
  1544.     addq.b    #3,d0
  1545.     bclr    d0,ciabprb    ;select drive X
  1546.     bset    d2,MotorState(A_DEVICE)
  1547.  
  1548.     moveq    #4,d2
  1549. .Wait:    btst    #CIAB_DSKRDY,ciaapra
  1550.     beq    .End
  1551.     move.l    #100*1000,d0
  1552.     bsr    delay
  1553.     dbra    d2,.Wait    
  1554.  
  1555. .End:    movem.l    (sp)+,d0/d2
  1556.     rts
  1557.  
  1558. Deselect:    or.b    #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb    ;Deselect all drives
  1559.     rts
  1560.  
  1561. SelectDriveSameMotor:
  1562. ;Select a drive WITHOUT changing the current motor state
  1563. ;Selects UnitNum(A_UNIT).
  1564.  
  1565.     push    d2
  1566.     move.b    UnitNum(A_UNIT),d2
  1567.     bsr    Deselect
  1568.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1569.     btst    d2,MotorState(A_DEVICE)
  1570.     bne    .WasOn
  1571.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1572. .WasOn:    addq.b    #3,d2
  1573.     bclr    d2,ciabprb    ;select drive X
  1574.     pop    d2
  1575. _RTS:    rts
  1576.  
  1577. DISK_Update:
  1578. ;Flush track buffer if "dirty"
  1579.  
  1580.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1581.     beq    _RTS
  1582.     movem.l    d0-d2/a0/a2/a6,-(sp)
  1583.     move.l    #_custom,a6
  1584.  
  1585. ;This is somewhat tricky, because the currently selected drive may not be
  1586. ;the drive that we want to write to!
  1587.     move.b    UnitNum(A_UNIT),d0
  1588.     move.w    TDU_CURRTRK(A_UNIT),d1
  1589.     move.b    BufferDrive(A_DEVICE),UnitNum(A_UNIT)    ;fake drive
  1590.     move.b    BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT)    ;fake track
  1591.     bsr    SelectDrive
  1592.     bsr    SelectSide
  1593.  
  1594. ;The logic of this routine is complicated by the need to support the write
  1595. ;optimization scheme (see DISK_Write for details). What we need to do is
  1596. ;check the WriteMap to determine whether a ReadTrackAndDecode is required
  1597. ;before finally writing...
  1598.     move.l    WriteMap(A_DEVICE),d2
  1599.     and.l    SectorMask(A_UNIT),d2
  1600.     cmp.l    SectorMask(A_UNIT),d2
  1601.     beq    .SkipRead
  1602.     move.l    DecodedBuffer(A_DEVICE),a0
  1603.     bsr    ReadTrackAndDecode
  1604.     tst.l    d7
  1605.     bne    .End
  1606. .SkipRead:
  1607.     move.l    d7,WriteMap(A_DEVICE)
  1608.  
  1609.     move.l    DecodedBuffer(A_DEVICE),a2
  1610.     bsr    EncodeAndWriteTrack
  1611.     move.b    d0,UnitNum(A_UNIT)
  1612.     move.w    d1,TDU_CURRTRK(A_UNIT)
  1613.     bsr    SelectDrive
  1614. .End:    movem.l    (sp)+,d0-d2/a0/a2/a6
  1615.     rts
  1616.  
  1617. SelectSide:
  1618. ;Select the correct side based on current track. This routine MUST be
  1619. ;called by certain routines (e.g. Update, Write) because the side select is
  1620. ;lost after a GiveUnit!
  1621.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1622.     btst    #0,TDU_CURRTRK+1(A_UNIT)
  1623.     bne    .skip
  1624.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1625. .skip:    push    d0
  1626.     move.l    #2*1000,d0
  1627.     bsr    delay
  1628.     pop    d0
  1629.     rts
  1630.  
  1631. ;Enter with destination track in d3.b
  1632.  
  1633. ;Note: This routine checks for valid track numbers -- if either the
  1634. ;current track number or the destination is invalid, PANIC.
  1635.  
  1636.     IFND    DEVPAC
  1637. SaveRegs    setrl    d0/d3-d4
  1638.     ENDC
  1639.  
  1640. SeekNoUpdate:
  1641. ;Perform a seek but don't call DISK_Update -- used by main routine when
  1642. ;checking for disk changes.
  1643.  
  1644.     IFD    DEVPAC
  1645.     movem.l    d0/d3-d4,-(sp)
  1646.     ELSE
  1647.     movem.l    SaveRegs,-(sp)
  1648.     ENDC
  1649.     bra    SeekAfterUpdate
  1650.  
  1651. MinSeek:
  1652. ;Only call MinSeek if it is certain that the side select has not been
  1653. ;changed!
  1654.     cmp.b    TDU_CURRTRK+1(A_UNIT),d3
  1655.     bne    Seek
  1656.     move.w    d0,-(sp)
  1657.     move.b    BufferDrive(A_DEVICE),d0
  1658.     cmp.b    UnitNum(A_UNIT),d0
  1659.     bne    .Seek
  1660.     move.w    (sp)+,d0
  1661.     rts
  1662. .Seek:    move.w    (sp)+,d0
  1663.  
  1664. Seek:
  1665.     IFD    DEVPAC
  1666.     movem.l    d0/d3-d4,-(sp)
  1667.     ELSE
  1668.     movem.l    SaveRegs,-(sp)
  1669.     ENDC
  1670.  
  1671.     bsr    DISK_Update
  1672. SeekAfterUpdate:
  1673.     tst.l    d7
  1674.     bne    .End
  1675.  
  1676.     move.l    TDU_STEPDELAY(A_UNIT),d0
  1677.     cmp.b    NumTracks(A_UNIT),d3
  1678.     bhs    .Error
  1679.  
  1680. ;Set head
  1681.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1682.     btst    #0,d3
  1683.     bne    .skip
  1684.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1685. .skip:
  1686.     move.w    TDU_CURRTRK(A_UNIT),d4
  1687.     lsr.b    #1,d4
  1688.     cmp.b    #NUMTRACKS,d4
  1689.     bhs    .Error
  1690.     move.b    d3,TDU_CURRTRK+1(A_UNIT)
  1691.     lsr.b    #1,d3
  1692.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  1693.     sub.b    d3,d4
  1694.     beq    .SideSelectOnly
  1695.     bhi    .StepIn    ;Go if DISK_CurrentTrack > Destination
  1696.     bclr    #CIAB_DSKDIREC,ciabprb    ;set to "in" (higher tracks)
  1697.     neg.b    d4
  1698. .StepIn:
  1699.     bsr    SelectDriveSameMotor
  1700.     bset    #CIAB_DSKSTEP,ciabprb
  1701.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  1702.     bset    #CIAB_DSKSTEP,ciabprb
  1703.     bsr    Deselect
  1704.     bsr    delay
  1705.     subq.b    #1,d4
  1706.     bne    .StepIn
  1707.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  1708.     bsr    delay
  1709.     bsr    SelectDriveSameMotor
  1710. .End:
  1711.     IFD    DEVPAC
  1712.     movem.l    (sp)+,d0/d3-d4
  1713.     ELSE
  1714.     movem.l    (sp)+,SaveRegs
  1715.     ENDC
  1716.     rts
  1717. .SideSelectOnly:
  1718.     move.l    #2*1000,d0
  1719.     bsr    delay
  1720.     bra    .End
  1721. .Error:
  1722. ;    move.w    #$f00,$dff180
  1723.     bra    .End
  1724.  
  1725. ;*************************** Delay code ****************************
  1726.  
  1727. delay:
  1728.  
  1729. ;Enter with microseconds in D0.L
  1730.  
  1731.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1732.     lea    TimerIORequest(A_DEVICE),a1
  1733.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1734.     clr.l    TV_SECS+IO_SIZE(a1)
  1735.     move.l    d0,TV_MICRO+IO_SIZE(a1)
  1736.     move.l    4,a6
  1737.     SYS    DoIO
  1738.  
  1739. ;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
  1740.     lea    TimerIORequest(A_DEVICE),a1
  1741.     move.l    MN_REPLYPORT(a1),a0
  1742.     move.b    MP_SIGBIT(a0),d0
  1743.     moveq    #0,d1
  1744.     bset    d0,d1
  1745.     moveq    #0,d0
  1746.     SYS    SetSignal
  1747.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1748.     rts
  1749.  
  1750. TimeOutWait:
  1751.  
  1752. ;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
  1753. ;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
  1754.  
  1755.     movem.l    d1-d4/a0-a2/a6,-(sp)
  1756.     move.l    d0,d3
  1757.     move.l    4,a6
  1758.     lea    TimerIORequest(A_DEVICE),a1
  1759.     move.l    a1,a2
  1760.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1761.     clr.l    TV_SECS+IO_SIZE(a1)
  1762.     move.l    d1,TV_MICRO+IO_SIZE(a1)
  1763.     move.l    MN_REPLYPORT(a1),a0
  1764.     moveq    #0,d2
  1765.     move.b    MP_SIGBIT(a0),d1
  1766.     bset    d1,d2
  1767.     SYS    SendIO
  1768.     move.l    d3,d0
  1769.     add.l    d2,d0    ;equivilent to OR in this case
  1770.     SYS    Wait
  1771.     and.l    d3,d0    ;exclude timer signal
  1772.     move.l    d0,d4
  1773.     beq    .TimeOut    ;if zero, timer signal was the only signal
  1774.     move.l    a2,a1
  1775.     SYS    AbortIO
  1776. .TimeOut:    move.l    a2,a1
  1777.     SYS    WaitIO
  1778.     moveq    #0,d0    ;value
  1779.     move.l    d2,d1    ;mask (timer sig)
  1780.     SYS    SetSignal    ;make SURE the timer signal is clear
  1781.     move.l    d4,d0
  1782.     movem.l    (sp)+,d1-d4/a0-a2/a6
  1783.     rts
  1784.  
  1785. ;*************************** Interrupt routines ************************
  1786.  
  1787. ;------------------------------------------
  1788. ;Register contents upon entering a handler:
  1789. ;D1 - (INTENAR) AND (INTREQR)
  1790. ;A0 - $dff000
  1791. ;A1 - data ptr (device ptr in this case)
  1792. ;A6 - ExecBase 
  1793.  
  1794. ;d0, d1, a0, a1, a5, and a6 are scratch.
  1795. ;-----------------------------------------
  1796.  
  1797. ;However, the disk.resource autodoc indicates the following arrangement for
  1798. ;interrupts installed by GetUnit:
  1799.  
  1800. ;D0/D1/A0/A1 are scratch
  1801. ;A1 points to IS_DATA (device pointer in this case).
  1802. ;Make no other assumptions!
  1803.  
  1804. SyncInt:    addq.w    #1,SyncCount(a1)
  1805.     move.l    SyncSig(a1),d0
  1806.     lea    Task(a1),a1
  1807.     push    a6
  1808.     move.l    4,a6
  1809.     SYS    Signal
  1810.     pop    a6
  1811.  
  1812. ;Delay approximately 63us to avoid two sync interrupts
  1813. ;This could be improved.
  1814.     moveq    #1,d0
  1815. .L1:    move.b    vhposr+_custom,d1
  1816. .L2:    cmp.b    vhposr+_custom,d1
  1817.     beq    .L2
  1818.     dbra    d0,.L1
  1819.     move.w    #INTF_DSKSYNC,intreq+_custom    ;clear sync
  1820.     rts    
  1821.  
  1822. BlockInt:
  1823.     push    a6
  1824.     move.l    #_custom,a6
  1825.     move.w    #INTF_DSKBLK,intreq(a6)
  1826.  
  1827. ;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
  1828.     bclr    #DEVB_Verify,DEV_FLAGS(a1)
  1829.     beq    .NoVerify
  1830.  
  1831.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  1832.     move.l    VerifyBuffer(a1),dskpt(a6)
  1833.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1834.     move.w    #0,dsklen(a6)
  1835.     move.w    BlockIntDskLen(a1),dsklen(a6)
  1836.     move.w    BlockIntDskLen(a1),dsklen(a6)
  1837.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1838. ;Now the first sync interrupt we get will be known to be valid, because the
  1839. ;DMA read is fully started.
  1840.     bra    .End
  1841.  
  1842. .NoVerify:
  1843.     move.l    BlockSig(a1),d0
  1844.     lea    Task(a1),a1
  1845.     move.l    4,a6
  1846.     SYS    Signal
  1847. .End:    pop    a6
  1848.     rts
  1849.  
  1850. IndexInt:
  1851.     move.w    IndexDskLen(a1),d0
  1852.     beq    .End
  1853.     move.w    d0,dsklen+_custom
  1854.     move.w    d0,dsklen+_custom
  1855.     clr.w    IndexDskLen(a1)
  1856. .End:    rts
  1857.  
  1858. EnableIndex:
  1859.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1860.     move.l    CIABase(A_DEVICE),a6
  1861.     moveq    #16,d0
  1862.     SYS    SetICR    ;clear interrupt
  1863.     move.l    #16+128,d0
  1864.     SYS    AbleICR    ;enable interrupt
  1865.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1866.     rts
  1867. DisableIndex:
  1868.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1869.     move.l    CIABase(A_DEVICE),a6
  1870.     moveq    #16,d0
  1871.     SYS    AbleICR    ;disable interrupt
  1872.     moveq    #16,d0
  1873.     SYS    SetICR    ;clear interrupt
  1874.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1875.     rts
  1876.  
  1877. ;*********************** End interrupt routines ************************
  1878.  
  1879. ClearSigs:
  1880.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1881.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom    ;clear sync+done
  1882.     clr.w    SyncCount(A_DEVICE)
  1883.     clr.w    IndexDskLen(A_DEVICE)
  1884.     move.l    4,a6
  1885.     moveq    #0,d0    ;value
  1886.     move.l    SyncSig(A_DEVICE),d1
  1887.     or.l    BlockSig(A_DEVICE),d1    ;hs (add)
  1888.     SYS    SetSignal
  1889.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1890.     rts
  1891.  
  1892. SetRegs:
  1893. ;This routine enables the block interrupt, but leaves sync interrupts
  1894. ;disabled.
  1895.  
  1896.     move.l    RawBuffer(A_DEVICE),dskpt(a6)
  1897.     move.w    #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
  1898.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
  1899.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1900.     bsr    ClearSigs
  1901.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable block int
  1902.     rts
  1903.  
  1904. PreComp:
  1905. ;Enter with $dff000 in A6
  1906.     movem.l    d0-d1,-(sp)
  1907.     move.w    #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)    ;no precomp
  1908.     move.w    TDU_CURRTRK(A_UNIT),d0
  1909.     cmp.w    TDU_COMP01TRACK(A_UNIT),d0
  1910.     bls    .End
  1911.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0,d1
  1912.     cmp.w    TDU_COMP10TRACK(A_UNIT),d0
  1913.     bls    .Done
  1914.     move.w    #ADKF_SETCLR+ADKF_PRECOMP1,d1
  1915.     cmp.w    TDU_COMP11TRACK(A_UNIT),d0
  1916.     bls    .Done
  1917.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
  1918. .Done:    move.w    d1,adkcon(a6)
  1919. .End:    movem.l    (sp)+,d0-d1
  1920.     rts
  1921.  
  1922. ScanSync:
  1923. ;Scan for sync mark
  1924. ;A2 - pointer to raw data -- updated
  1925. ;Error code in D7
  1926.  
  1927.     bsr    WaitWordSync
  1928.     tst.l    d7
  1929.     bne    .End
  1930.  
  1931. ;This routine is trickier than it appears. The trick is that we must NOT
  1932. ;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
  1933. ;the DMA starts in the middle of the first sync word. The second sync word
  1934. ;is thrown away by the hardware. It sounds exotic, but it actually happens
  1935. ;quite often!
  1936.  
  1937.     push    a0
  1938.     move.l    RawBuffer(A_DEVICE),a0
  1939.     cmp.l    a0,a2
  1940.     add.l    RawBufSize(A_UNIT),a0
  1941.     beq    .found    ;if start of buffer, don't scan for sync!!!
  1942.  
  1943. .Sync:    cmpi.w    #$4489,(a2)+
  1944.     beq    .found
  1945.     cmp.l    a2,a0
  1946.     bhi    .Sync
  1947. .Error:    pop    a0
  1948.     moveq    #DISK_NoSync,d7
  1949.     rts
  1950. .found:    cmpi.w    #$4489,(a2)
  1951.     bne    .ok
  1952.     addq.l    #2,a2
  1953.     cmp.l    a2,a0
  1954.     bhi    .found
  1955.     bra    .Error
  1956. .ok:    pop    a0
  1957. .End:    rts
  1958.  
  1959. StopDMA:    move.w    #0,dsklen(a6)
  1960.     move.w    #1<<15,dsklen(a6)
  1961.     move.w    #1<<15,dsklen(a6)    ;zero-length DMA transfer
  1962.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  1963.     bra    ClearSigs
  1964.  
  1965. WaitWordSync:
  1966. ;Wait for a sync mark or disk block done
  1967. ;Will return an error in 300ms if nothing happens.
  1968.  
  1969.     movem.l    d0-d1,-(sp)
  1970. .Wait:    tst.w    SyncCount(A_DEVICE)
  1971.     bne    .Sync
  1972.     move.l    SyncSig(A_DEVICE),d0
  1973.     or.l    BlockSig(A_DEVICE),d0    ;hs (add)
  1974.     move.l    #300*1000*2,d1    ;time-out (300ms)
  1975.     bsr    TimeOutWait
  1976.     beq    .Error
  1977.     and.l    BlockSig(A_DEVICE),d0
  1978.     bne    .Done
  1979.     bra    .Wait
  1980. .Sync:    subq.w    #1,SyncCount(A_DEVICE)
  1981. .End:    movem.l    (sp)+,d0-d1
  1982.     rts
  1983. .Done:    move.w    #$C000,SyncCount(A_DEVICE)    ;this is obscure - should change
  1984.     bra    .End
  1985. .Error:    moveq    #DISK_NoSync,d7
  1986.     bra    .End
  1987.  
  1988. ReadTrackAndDecodeBuffered:
  1989.     push    d0
  1990.     move.l    WriteMap(A_DEVICE),d0
  1991.     beq    .OK    ;no optimized writes to worry about
  1992.     and.l    SectorMask(A_UNIT),d0
  1993.     cmp.l    SectorMask(A_UNIT),d0
  1994.     beq    .End
  1995.     moveq    #-1,d0
  1996.     move.l    d0,WriteMap(A_DEVICE)    ;mark sectors as filled
  1997.     bra    .ReadTrack
  1998.  
  1999. .OK:    move.b    UnitNum(A_UNIT),d0
  2000.     cmp.b    BufferDrive(A_DEVICE),d0
  2001.     bne    .ReadTrack
  2002.     move.w    TDU_CURRTRK(A_UNIT),d0
  2003.     cmp.b    BufferTrack(A_DEVICE),d0
  2004.     bne    .ReadTrack
  2005. .End:    pop    d0
  2006.     rts
  2007. .ReadTrack:
  2008.     pop    d0
  2009.  
  2010. ReadTrackAndDecode:
  2011.  
  2012. ;Input: Buffer ptr in A0
  2013. ;Returns error code in D7
  2014.  
  2015.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  2016.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  2017.  
  2018. ReadTrackAndDecodeNoBuffer:
  2019.     move.w    d0,-(sp)
  2020.     move.b    TDU_RETRYCNT(A_UNIT),d0
  2021. .Retry:    moveq    #0,d7
  2022.     bsr    ReadTrackAndDecodeNoRetry
  2023.     tst.l    d7
  2024.     beq    .End
  2025.     subq.b    #1,d0
  2026.     bpl    .Retry
  2027. ;Could not recover from error - clear the buffer
  2028.     bsr    Clear
  2029.  
  2030. .End:    move.w    (sp)+,d0
  2031.     rts
  2032.  
  2033. ReadTrackAndDecodeNoRetry:
  2034.     movem.l    d0-d6/a0-a2,-(sp)
  2035.  
  2036. ;Initiate a raw track read
  2037. ;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
  2038.  
  2039.     bsr    SetRegs
  2040.     move.w    #$0123,dsksync(a6)    ;set invalid sync word
  2041. ;At this point sync interrupts will stop happenning, because $0123 will
  2042. ;(hopefully!) never occur. So, we clear the interrupt and enable it.
  2043.     bsr    ClearSigs
  2044.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  2045.     move.w    #0,dsklen(a6)
  2046.     move.w    ReadDskLen(A_UNIT),dsklen(a6)
  2047.     move.w    ReadDskLen(A_UNIT),dsklen(a6)
  2048.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  2049.  
  2050. ;Now the first sync interrupt we get will be known to be valid, because the
  2051. ;DMA read is fully started.
  2052.  
  2053. ;Decode track
  2054.     move.l    RawBuffer(A_DEVICE),a2
  2055.  
  2056.     move.l    SectorsPerTrack(A_UNIT),d3
  2057.     subq.l    #1,d3
  2058.     move.l    #$55555555,d2
  2059.  
  2060. ;Wait for first sync marks...
  2061.     bsr    WaitWordSync
  2062.     tst.l    d7
  2063.     bne    .End
  2064.  
  2065. .SecLoop:
  2066.     bsr    ScanSync
  2067.     tst.l    d7
  2068.     bne    .End
  2069.  
  2070.     bsr    DecodeLong    ;get header
  2071.     subq.l    #8,a2
  2072.  
  2073.     and.w    #$ff00,d0    ;mask off sector number
  2074.     move.l    d0,d1
  2075.     cmp.w    MaxValidSec(A_UNIT),d1
  2076.     bhi    .HeaderError
  2077.     add.w    d1,d1    ;convert to sector offset
  2078.     lsr.w    #4,d0    ;get sector label offset
  2079.     lea    SectorLabels(A_DEVICE),a1
  2080.     lea    (a1,d0.w),a1
  2081.  
  2082. ;This code supports the write optimization...
  2083.     lsr.w    #4,d0    ;sector number
  2084.     move.l    WriteMap(A_DEVICE),d4
  2085.     btst    d0,d4    ;should we avoid reading this sector?
  2086.     bne    .EndLoop    ;yes, skip to next sector
  2087.  
  2088.     move.l    d1,-(sp)
  2089.     moveq    #0,d5
  2090.     move.l    (a2)+,d0
  2091.     eor.l    d0,d5
  2092.     move.l    (a2)+,d0
  2093.     eor.l    d0,d5
  2094. ;Decode and checksum sector label
  2095.     moveq    #3,d6
  2096. .Label:    move.l    16(a2),d4
  2097.     eor.l    d4,d5
  2098.     and.l    d2,d4
  2099.     move.l    (a2)+,d1
  2100.     eor.l    d1,d5
  2101.     and.l    d2,d1
  2102.     add.l    d1,d1
  2103.     or.l    d1,d4
  2104.     move.l    d4,(a1)+
  2105.     dbra    d6,.Label
  2106.     and.l    d2,d5
  2107.     lea    16(a2),a2    ;point at header checksum
  2108.     move.l    (sp)+,d1
  2109.     bsr    DecodeLong    ;header checksum
  2110.     cmp.l    d0,d5
  2111.     bne    .HeaderError
  2112.  
  2113. ;Verify track position
  2114.     swap    d1
  2115.     cmp.b    TDU_CURRTRK+1(A_UNIT),d1
  2116.     bne    .WrongTrack
  2117.     swap    d1
  2118.  
  2119.     bsr    DecodeLong    ;data area checksum
  2120.     lea    (a0,d1.w),a1    ;compute destination
  2121.  
  2122. ;Decode and checksum data block
  2123.     moveq    #127,d6
  2124.     moveq    #0,d5
  2125. .L1:    move.l    512(a2),d4
  2126.     eor.l    d4,d5
  2127.     and.l    d2,d4
  2128.     move.l    (a2)+,d1
  2129.     eor.l    d1,d5
  2130.     and.l    d2,d1
  2131.     add.l    d1,d1
  2132.     or.l    d1,d4
  2133.     move.l    d4,(a1)+
  2134.     dbra    d6,.L1
  2135.     and.l    d2,d5
  2136.     lea    512(a2),a2
  2137.  
  2138.     cmp.l    d0,d5    ;check data area checksum
  2139.     bne    .DataError
  2140. .EndLoop:    dbra    d3,.SecLoop
  2141.  
  2142. .End:    bsr    StopDMA
  2143.     movem.l    (sp)+,d0-d6/a0-a2
  2144.     rts
  2145. .HeaderError:
  2146.     moveq    #DISK_BadHeader,d7
  2147.     bra    .End
  2148. .DataError:
  2149.     moveq    #DISK_BadData,d7
  2150.     bra    .End
  2151. .WrongTrack:
  2152.     moveq    #TDERR_SeekError,d7
  2153.     move.w    TDU_CURRTRK(A_UNIT),d3
  2154.     bsr    SeekZero
  2155.     bsr    SelectDrive
  2156.     bsr    SeekNoUpdate
  2157.     bra    .End
  2158.  
  2159. DecodeLong:
  2160. ;A2 - ptr to buffer -- updated
  2161. ;D2 - $55555555
  2162. ;D0 - result
  2163.  
  2164.     move.l    d1,-(sp)
  2165.     move.l    (a2)+,d0
  2166.     move.l    (a2)+,d1
  2167.     and.l    d2,d0
  2168.     and.l    d2,d1
  2169.     add.l    d0,d0    ;was lsl.l #1,d0
  2170.     or.l    d1,d0
  2171.     move.l    (sp)+,d1
  2172.     rts
  2173.  
  2174. EncodeLong:
  2175. ;Enter with data to be encoded in D0.L
  2176. ;and pointer to destination in A0 -- updated
  2177. ;Exit with checksum in D5
  2178.  
  2179.     movem.l    d0-d4,-(sp)
  2180.     moveq    #0,d5
  2181.     move.l    #$55555555,d4
  2182.     move.l    d0,d3
  2183.     lsr.l    #1,d0
  2184.     bsr    Encode
  2185.     move.l    d3,d0
  2186.     bsr    Encode
  2187.     and.l    #$55555555,d5
  2188.     movem.l    (sp)+,d0-d4
  2189.     rts
  2190.  
  2191. Encode:
  2192. ;Enter with longword to code in D0.L and #$55555555 in D4
  2193. ;uses d0,d1,d2,a0 -- not saved
  2194.  
  2195. ;Accumulates checksum in D5
  2196.  
  2197.     and.l    d4,d0
  2198.     move.l    d0,d2
  2199.     eor.l    d4,d2
  2200.     move.l    d2,d1
  2201.     add.l    d2,d2
  2202.     lsr.l    #1,d1
  2203.     bset    #31,d1
  2204.     and.l    d2,d1
  2205.     or.l    d1,d0
  2206.     btst    #0,-1(a0)
  2207.     beq    .ok
  2208.     bclr    #31,d0
  2209. .ok:    eor.l    d0,d5
  2210.     move.l    d0,(a0)+
  2211.     rts
  2212.  
  2213. EncodeBlock:
  2214. ;Destination is always chip RAM (RawBuffer).
  2215. ;Source could be in chip RAM or fast RAM (in A2).
  2216.  
  2217.     movem.l    d0-d1/a0-a1/a6,-(sp)
  2218.     move.l    4,a6
  2219.     move.l    a2,a1
  2220.     SYS    TypeOfMem
  2221.     and.l    #MEMF_CHIP,d0
  2222.     bne    .Chip
  2223.     movem.l    (sp)+,d0-d1/a0-a1/a6
  2224.     bra    EncodeBlockCPU
  2225. .Chip:    movem.l    (sp)+,d0-d1/a0-a1/a6
  2226.     bra    EncodeBlockBlit
  2227.  
  2228. EncodeBlockCPU:
  2229. ;Enter with pointer to source data in A2 -- updated
  2230. ;Enter with pointer to destination in A0 -- updated
  2231.  
  2232. ;Exit with checksum in D5
  2233.     move.l    d6,-(sp)
  2234.     moveq    #0,d5
  2235.     move.w    #(512/4)-1,d6
  2236.  
  2237. EncodeBlockSub:
  2238. ;Number of longwords to encode (minus one) in D6.w
  2239.     movem.l    d0-d4,-(sp)
  2240.  
  2241. ;Encode odd bits
  2242.     push    a2
  2243.     move.w    d6,d3
  2244.     move.l    #$55555555,d4
  2245. .L1:    move.l    (a2)+,d0
  2246.     lsr.l    #1,d0
  2247.     bsr    Encode
  2248.     dbra    d3,.L1
  2249.  
  2250. ;Encode even bits
  2251.     pop    a2
  2252.     move.w    d6,d3
  2253. .L2:    move.l    (a2)+,d0
  2254.     bsr    Encode
  2255.     dbra    d3,.L2
  2256.     and.l    #$55555555,d5
  2257.     movem.l    (sp)+,d0-d4
  2258.     move.l    (sp)+,d6
  2259.     rts
  2260.  
  2261. EncodeSectorLabels:
  2262. ;D5 (checksum) must be initialized by caller
  2263.     move.l    d6,-(sp)
  2264.     move.w    #(16/4)-1,d6
  2265.     bra    EncodeBlockSub
  2266.  
  2267. EncodeBlockBlit:
  2268. ;Enter with pointer to source data in A2 -- updated
  2269. ;Enter with pointer to destination in A0 -- updated
  2270.  
  2271. ;Exit with checksum in D5
  2272.  
  2273.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2274.     push    a0
  2275.     move.l    GraphBase(A_DEVICE),a6
  2276.     SYS    OwnBlitter
  2277.     move.l    (sp),a0
  2278.     move.l    #_custom,a1
  2279.  
  2280.     move.w    #$808,d0    ;BLTSIZE
  2281.  
  2282.     SYS    WaitBlit
  2283.  
  2284.     move.w    #$ffff,bltafwm(a1)
  2285.     move.w    #$ffff,bltalwm(a1)
  2286.     clr.w    bltbmod(a1)
  2287.     clr.w    bltamod(a1)
  2288.     clr.w    bltdmod(a1)
  2289.     move.w    #$5555,bltcdat(a1)
  2290.  
  2291.     move.l    a2,bltbpt(a1)
  2292.     move.l    a2,bltapt(a1)
  2293.     move.l    a0,bltdpt(a1)
  2294.     move.w    #$1db1,bltcon0(a1)
  2295.     clr.w    bltcon1(a1)
  2296.     move.w    d0,bltsize(a1)
  2297.  
  2298.     SYS    WaitBlit
  2299.  
  2300.     move.l    a0,bltbpt(a1)
  2301.     move.l    a2,bltapt(a1)
  2302.     move.l    a0,bltdpt(a1)
  2303.     move.w    #$2d8c,bltcon0(a1)
  2304.     move.w    d0,bltsize(a1)
  2305.     movem.l    a0/a2,-(sp)
  2306.     lea    510(a2),a2    ;ptr to end of src
  2307.     lea    1022(a0),a0
  2308.  
  2309.     SYS    WaitBlit
  2310.  
  2311.     move.l    a2,bltbpt(a1)    ;src end
  2312.     move.l    a2,bltapt(a1)    ;src end
  2313.     move.l    a0,bltdpt(a1)    ;dst end
  2314.     move.w    #$0db1,bltcon0(a1)
  2315.     move.w    #$1002,bltcon1(a1)    ;decrement
  2316.     move.w    d0,bltsize(a1)
  2317.     movem.l    (sp)+,a0/a2
  2318.     lea    512(a0),a0
  2319.  
  2320.     SYS    WaitBlit
  2321.  
  2322.     move.l    a0,bltbpt(a1)
  2323.     move.l    a2,bltapt(a1)
  2324.     move.l    a0,bltdpt(a1)
  2325.     move.w    #$1d8c,bltcon0(a1)
  2326.     clr.w    bltcon1(a1)
  2327.     move.w    d0,bltsize(a1)
  2328.     pop    a0
  2329.  
  2330.     SYS    WaitBlit
  2331.  
  2332.     bsr    Correct
  2333.     lea    512(a0),a0
  2334.     bsr    Correct
  2335.     lea    -512(a0),a0
  2336.  
  2337.     move.w    #(1024/4)-1,d0
  2338.     move.l    #$55555555,d2
  2339.     moveq    #0,d5
  2340. ..    move.l    (a0)+,d1
  2341.     eor.l    d1,d5
  2342.     dbra    d0,..
  2343.     and.l    d2,d5
  2344.  
  2345.     SYS    DisownBlitter
  2346.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2347.     lea    512(a2),a2    ;update source pointer
  2348.     lea    1024(a0),a0    ;update destination pointer
  2349.     rts
  2350.  
  2351. ;This routine corrects the high bit of the current byte based on the
  2352. ;low bit of the previous byte.
  2353.  
  2354. Correct:
  2355.     push    d0
  2356.     move.b    (a0),d0
  2357.     btst    #0,-1(a0)
  2358.     bne    .ResetClock
  2359.     btst    #6,d0
  2360.     bne    .end
  2361.     bset    #7,d0
  2362.     bra    .end1
  2363. .ResetClock:
  2364.     bclr    #7,d0
  2365. .end1:    move.b    d0,(a0)
  2366. .end:    pop    d0
  2367.     rts
  2368.  
  2369. DISK_Wait:
  2370. ;Assumes $dff000 in A6.
  2371.     movem.l    d0-d1,-(sp)
  2372.     tst.w    SyncCount(A_DEVICE)
  2373.     bmi    .OK    ;if WaitWordSync detected a BlockSig, don't wait!
  2374.     move.l    BlockSig(A_DEVICE),d0
  2375.     move.l    #300*1000*2,d1
  2376.     bsr    TimeOutWait
  2377.     bne    .OK
  2378.     moveq    #DISK_NoSync,d7
  2379. .OK:    move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  2380.     bsr    ClearSigs
  2381.     movem.l    (sp)+,d0-d1
  2382.     rts
  2383.  
  2384. EncodeAndWriteTrack:
  2385. ;Enter with pointer to source data in A2
  2386.  
  2387.     movem.l    d0-d6/a0-a2,-(sp)
  2388.  
  2389.     btst    #CIAB_DSKPROT,ciaapra    ;check write protect status
  2390.     beq    .Protected
  2391.  
  2392.     move.l    RawBuffer(A_DEVICE),a0
  2393.  
  2394. ;Gap = 1660 bytes - 2 bytes for hardware bug
  2395.     move.l    #$aaaaaaaa,d1    ;10101010...
  2396.     move.w    GapCount(A_UNIT),d0    ;414/829
  2397. ..    move.l    d1,(a0)+
  2398.     dbra    d0,..
  2399.     subq.l    #2,a0    ;leave room for 2 extra bytes at the very end
  2400.  
  2401.     move.l    SectorsPerTrack(A_UNIT),d1    ;number of sectors
  2402.     moveq    #0,d3    ;sector count
  2403. .SecLoop:
  2404.     move.l    #$aaaaaaaa,(a0)
  2405.     bsr    Correct
  2406.     addq.l    #4,a0
  2407.     move.l    #$44894489,(a0)+
  2408.     move.l    #$ff000000,d0
  2409.     moveq    #0,d6
  2410.     move.w    TDU_CURRTRK(A_UNIT),d6
  2411.     swap    d6
  2412.     or.l    d6,d0
  2413.     move.l    d3,d6
  2414.     lsl.l    #8,d6
  2415.     or.l    d6,d0
  2416.     or.l    d1,d0
  2417.     bsr    EncodeLong    ;header
  2418.  
  2419. ;Encode sector label
  2420.     push    a2
  2421.     lea    SectorLabels(A_DEVICE),a2
  2422.     move.l    d3,d0
  2423.     lsl.l    #4,d0    ;sector*16
  2424.     lea    (a2,d0.w),a2
  2425.     bsr    EncodeSectorLabels
  2426.     pop    a2
  2427.  
  2428.     move.l    d5,d0
  2429.     bsr    EncodeLong    ;header checksum
  2430.     move.l    a0,d2    ;save raw data pointer
  2431.     addq.l    #8,a0
  2432.     bsr    EncodeBlock    ;encode data block
  2433.     move.l    d5,d0
  2434.     exg    a0,d2
  2435.     bsr    EncodeLong    ;data block checksum
  2436.     bsr    Correct
  2437.     move.l    d2,a0
  2438.     addq.l    #1,d3
  2439.     subq.l    #1,d1
  2440.     bne    .SecLoop
  2441.  
  2442.     move.w    #$aaa8,(a0)
  2443.     bsr    Correct    ;extra word to avoid hardware bug
  2444.  
  2445. ;Physically write the data
  2446. .WriteAgain:
  2447.     bsr    SetRegs
  2448.     bsr    PreComp
  2449.     move.w    #ADKF_WORDSYNC,adkcon(a6)    ;turn OFF wordsync!!!
  2450.     move.w    #$0123,dsksync(a6)
  2451.     bsr    ClearSigs
  2452.     move.w    VerifyDskLen(A_UNIT),BlockIntDskLen(A_DEVICE)
  2453.  
  2454.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2455.     beq    .SkipVerify
  2456.     bset    #DEVB_Verify,DEV_FLAGS(A_DEVICE)
  2457. .SkipVerify:
  2458.  
  2459.     move.l    VerifyBuffer(A_DEVICE),a0
  2460.     clr.l    (a0)+
  2461.     clr.l    (a0)
  2462.     move.w    #0,dsklen(a6)
  2463.     move.w    WriteDskLen(A_UNIT),dsklen(a6)
  2464.     move.w    WriteDskLen(A_UNIT),dsklen(a6)
  2465.  
  2466. ;This piece of code (commented out) tests the function of the rare
  2467. ;"interrupt delayed" requester. (I've never seen it appear in actual use).
  2468.     IFGT    0
  2469. ;TEST
  2470.     push    a6
  2471.     move.l    4,a6
  2472.     SYS    Disable
  2473.     move.w    #5000,d0
  2474. .L1:    move.b    vhposr+_custom,d1
  2475. .L2:    cmp.b    vhposr+_custom,d1
  2476.     beq    .L2
  2477.     dbra    d0,.L1
  2478.     SYS    Enable
  2479.     pop    a6
  2480.     ENDC
  2481.  
  2482.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2483.     beq    .NoVerify
  2484.  
  2485. ;VERIFY
  2486.  
  2487. ;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
  2488. ;and VerifyBuffer (what is coming in). Due to the ingenious method of
  2489. ;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
  2490. ;in the same order that we wrote it.
  2491.  
  2492.     move.l    VerifyBuffer(A_DEVICE),a0
  2493.     move.l    RawBuffer(A_DEVICE),a2
  2494.     add.w    FirstSector(A_UNIT),a2    ;1666/3326 - first sector minus sync
  2495.     bsr    WaitWordSync
  2496.     tst.l    d7
  2497.     bne    .VerifyError
  2498.     bsr    WaitWordSync
  2499.     tst.l    d7
  2500.     bne    .VerifyError
  2501. ;Now we have our first sector in the verify buffer. Scan for a
  2502. ;sync mark. (There may be 1 or 2 sync marks).
  2503.     cmp.w    #$4489,(a0)+
  2504.     bne    .VerifyError
  2505.     cmp.w    #$4489,(a0)
  2506.     bne    .HaveSync
  2507.     addq.l    #2,a0
  2508. .HaveSync:
  2509.  
  2510. ;We go through a rather elaborate procedure here to make sure that we've
  2511. ;started reading with sector 0. (If not, display a requester informing the
  2512. ;user that something is locking out level-1 interrupts for a long period of
  2513. ;time).
  2514.     push    a2
  2515.     move.l    #$55555555,d2
  2516.     move.l    a0,a2
  2517.     bsr    DecodeLong
  2518.     pop    a2
  2519.     and.w    #$ff00,d0
  2520.     beq    .Sector0
  2521.     move.l    a0,a1
  2522.     moveq    #9,d0
  2523.     moveq    #0,d1
  2524. .il2    move.l    (a1)+,d3
  2525.     eor.l    d3,d1
  2526.     dbra    d0,.il2
  2527.     and.l    d2,d1
  2528.     push    a2
  2529.     move.l    a1,a2
  2530.     bsr    DecodeLong
  2531.     pop    a2
  2532.     cmp.l    d0,d1
  2533.     bne    .VerifyError
  2534.  
  2535. ;Display an informational requester.
  2536.     bsr    StopDMA
  2537.     push    a6
  2538.     lea    IntName(pc),a1        ;hs
  2539.     SYS    OldOpenLibrary
  2540.     tst.l    d0
  2541.     beq.s    .WriteAgain2
  2542.     move.l    d0,a6
  2543.     cmp.w    #36,LIB_VERSION(a6)
  2544.     bhi    .KS20
  2545.  
  2546. ;Running under 1.3. Put up a DisplayAlert.
  2547.     moveq    #0,d0    ;alert type (recoverable)
  2548.     lea    .AlertLockOut(pc),a0
  2549.     moveq    #20,d1    ;height
  2550.     SYS    DisplayAlert
  2551. .WriteAgain3    move.l    a6,a1
  2552.     SYS    CloseLibrary
  2553. .WriteAgain2    pop    a6
  2554.     bra    .WriteAgain
  2555.  
  2556.  
  2557. ;Running under 2.0. Put up a EasyRequest.
  2558. .KS20:
  2559.     sub.l    a0,a0
  2560.     sub.l    a2,a2
  2561.     lea    .LockOut(pc),a1
  2562.     SYS    EasyRequestArgs
  2563.     bra.s    .WriteAgain3        ;/hs
  2564.  
  2565.  
  2566. ;Note: Probably should compare with the blitter, but this will be fairly
  2567. ;fast.
  2568.  
  2569. .Sector0:
  2570.     move.w    #270-1,d0
  2571. .il3    cmp.l    (a0)+,(a2)+
  2572.     dbne    d0,.il3
  2573.     bne    .VerifyError
  2574.  
  2575. ;Now we are over the initial hump of the first sync mark. The rest of the
  2576. ;compare is even easier.
  2577.     move.l    SectorsPerTrack(A_UNIT),d1
  2578.     subq.l    #3,d1
  2579. .VLoop:    bsr    WaitWordSync
  2580.     tst.l    d7
  2581.     bne    .VerifyError
  2582.     move.w    #272-1,d0
  2583. .il4    cmp.l    (a0)+,(a2)+
  2584.     dbne    d0,.il4
  2585.     bne    .VerifyError
  2586.     dbra    d1,.VLoop
  2587.  
  2588. ;We have one more sector to verify. This time we must wait for "Block
  2589. ;done", rather than another sync.
  2590.  
  2591.     bsr    DISK_Wait
  2592.     tst.l    d7
  2593.     bne    .VerifyError
  2594.     move.w    #272-1,d0
  2595. .il5    cmp.l    (a0)+,(a2)+
  2596.     dbne    d0,.il5
  2597.     bne    .VerifyError
  2598.     bra    .End
  2599.  
  2600. .VerifyError:
  2601.     moveq    #0,d7    ;don't propagate the error to the app
  2602. ;We go here if an error is detected during the verify. We first shut down
  2603. ;the read operation that may be in progress, then put up a requester and let
  2604. ;the user choose whether to retry or abort.
  2605.  
  2606.     bsr    StopDMA    ;stop!!
  2607.     push    a6
  2608.     lea    IntName(pc),a1        ;hs
  2609.     SYS    OldOpenLibrary
  2610.     tst.l    d0
  2611.     beq    .WriteAgain2
  2612.     move.l    d0,a6
  2613.     cmp.w    #36,LIB_VERSION(a6)
  2614.     bhi    .DoKS20
  2615.  
  2616. ;Running under 1.3. Put up a DisplayAlert.
  2617.     moveq    #0,d0    ;alert type (recoverable)
  2618.     lea    .AlertVError(pc),a0
  2619.     moveq    #20,d1    ;height
  2620.     SYS    DisplayAlert
  2621. ;D0 is set to 'TRUE' if the LEFT button was pressed.
  2622. .waend    move.l    a6,a1
  2623.     SYS    CloseLibrary
  2624.     pop    a6
  2625.     tst.l    d0
  2626.     bne    .WriteAgain    ;go if left (button/gadget) pressed
  2627.     bra    .End
  2628.  
  2629. ;Running under 2.0. Put up a EasyRequest.
  2630. .DoKS20:    sub.l    a0,a0
  2631.     sub.l    a2,a2
  2632.     lea    .VError(pc),a1
  2633.     push    a3
  2634.     moveq    #0,d0
  2635.     move.w    TDU_CURRTRK(A_UNIT),d0
  2636.     move.l    d0,-(sp)
  2637.     move.l    sp,a3
  2638.     SYS    EasyRequestArgs
  2639.     addq.l    #4,sp
  2640.     pop    a3
  2641.     bra.s    .waend
  2642. .NoVerify:
  2643.     bsr    DISK_Wait
  2644.     move.l    #4*1000,d0
  2645.     bsr    delay    ;post-write delay
  2646. .End:    movem.l    (sp)+,d0-d6/a0-a2
  2647.     rts
  2648. .Protected:
  2649.     moveq    #DISK_WriteProtected,d7
  2650.     bra    .End
  2651.  
  2652. .AlertVError:
  2653.     dc.w    10    ;x coordinate
  2654.     dc.b    10    ;y coordinate
  2655.     dc.b    '*** VERIFY ERROR !!! ***  Hit LEFT button to RETRY'
  2656.     dc.b    ', or RIGHT button to CANCEL.',0
  2657.     dc.b    0    ;continuation byte
  2658.     even
  2659.  
  2660. .AlertLockOut:
  2661.     dc.w    10    ;x coordinate
  2662.     dc.b    10    ;y coordinate
  2663.     dc.b    'Disk block interrupt delayed by >10ms.'
  2664.     dc.b    '  Press mouse button.',0
  2665.     dc.b    0    ;continuation byte
  2666.     even
  2667.  
  2668. .VError:    dc.l    es_SIZEOF
  2669.     dc.l    0
  2670.     dc.l    .Title
  2671.     dc.l    .MainText
  2672.     dc.l    .GadgetText
  2673. .LockOut:    dc.l    es_SIZEOF
  2674.     dc.l    0
  2675.     dc.l    .Title
  2676.     dc.l    .LockOutTxt
  2677.     dc.l    .Okay
  2678. .LockOutTxt:
  2679.     dc.b    'Disk block interrupt delayed by >10ms caused erroneous'
  2680.     dc.b    ' verify.',0
  2681. .Okay:    dc.b    'If you say so. Try it again!',0
  2682. .Title:    dc.b    'hackdisk.device message',0
  2683. .MainText:
  2684.     dc.b    '*** VERIFY ERROR!!! ***',$a,'Track %ld',0
  2685. .GadgetText:
  2686.     dc.b    'Retry|Cancel',0
  2687.     even
  2688.  
  2689. DISK_Write:
  2690.  
  2691. ;Error code returned in D7, as always.
  2692.  
  2693.     movem.l    d0-d5/a0-a2,-(sp)
  2694.  
  2695.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  2696.     bne    .ProtError
  2697.  
  2698.     move.l    d0,d5    ;length
  2699.  
  2700. ;The meat of the write routine...
  2701.  
  2702. .WriteLoop:
  2703.     move.l    d1,d3    ;offset
  2704.     divu.w    d6,d3    ;d3.w is track #
  2705.     move.l    d3,d4
  2706.     clr.w    d4
  2707.     swap    d4    ;d4.l is byte offset into track
  2708.     bsr    MinSeek
  2709.     tst.l    d4    ;any offset?
  2710.     bne    .Complex    ;yes
  2711.     cmp.l    d6,d5    ;at least a track left?
  2712.     blo    .Complex    ;no
  2713.     move.l    a0,d0
  2714.     btst    #0,d0    ;word aligned?
  2715.     bne    .Complex    ;no, do it the long way
  2716.     move.l    a0,a2
  2717.     bsr    EncodeAndWriteTrack
  2718.     tst.l    d7
  2719.     bne    .End
  2720.     sub.l    d6,d5    ;update length
  2721.     beq    .End
  2722.     add.l    d6,a0    ;update source pointer
  2723.     add.l    d6,d1    ;update offset
  2724.     bra    .WriteLoop
  2725.  
  2726. .Complex:
  2727.  
  2728. ;This part is somewhat difficult. We check the offset and length parameters
  2729. ;to see whether they're a multiple of 512. If so, we keep track of which
  2730. ;sectors will be written in the buffer. This information is later used by
  2731. ;Update to determine whether a part of the original track must be read in.
  2732. ;(We don't attempt this optimization if the user is writing some odd number
  2733. ;of bytes...This is probably why trackdisk has the limits that it does).
  2734.  
  2735.     tst.l    d5
  2736.     beq    .End    ;nothing left, forget it
  2737.     move.l    d4,d0
  2738.     and.w    #%111111111,d0
  2739.     bne    .NoOpt
  2740.     move.l    d4,d0
  2741.     move.l    d5,d2
  2742.     and.w    #%111111111,d2
  2743.     bne    .NoOpt
  2744.     move.l    d5,d2
  2745.     lsr.l    #8,d0
  2746.     lsr.l    #1,d0    ;get starting sector number
  2747.     lsr.l    #8,d2
  2748.     lsr.l    #1,d2    ;get length in sectors
  2749.     move.l    WriteMap(A_DEVICE),d7
  2750. .OptLoop:    bset    d0,d7
  2751.     addq.b    #1,d0
  2752.     cmp.b    #32,d0
  2753.     beq    .EOpt
  2754.     subq.l    #1,d2
  2755.     bne    .OptLoop
  2756. .EOpt:    move.l    d7,WriteMap(A_DEVICE)
  2757.     moveq    #0,d7
  2758.  
  2759. ;This is normally done by ReadTrackAndDecode.
  2760.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  2761.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  2762.     bra    .Opt    ;don't read (yet)
  2763.  
  2764. .NoOpt:    push    a0
  2765.     move.l    DecodedBuffer(A_DEVICE),a0
  2766.     bsr    ReadTrackAndDecodeBuffered
  2767.     pop    a0
  2768.     tst.l    d7
  2769.     bne    .End
  2770. .Opt:    move.l    DecodedBuffer(A_DEVICE),a1
  2771.     add.l    d4,a1    ;add byte offset into track
  2772.     sub.l    d6,d4
  2773.     neg.l    d4
  2774.     add.l    d4,d1    ;update offset
  2775.  
  2776. ;D4.L - number of bytes that could be transferred from this track
  2777. ;D5.L - number of bytes left in the entire Read request
  2778.  
  2779.     cmp.l    d5,d4
  2780.     bhs    .FinalWrite
  2781.     sub.l    d4,d5    ;update length
  2782.     move.l    d4,d0
  2783.     bsr    CopyMem
  2784.     add.l    d0,a0    ;update dest pointer
  2785.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2786.     bra    .WriteLoop
  2787.  
  2788. .FinalWrite:
  2789.     move.l    d5,d0
  2790.     bsr    CopyMem
  2791.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2792. .End:    movem.l    (sp)+,d0-d5/a0-a2
  2793.     rts
  2794. .ProtError:
  2795.     moveq    #DISK_WriteProtected,d7
  2796.     bra    .End
  2797.  
  2798. MotorOff:
  2799. ;MotorOff turns off all drive motors and leaves all drives deselected
  2800.     bsr    Deselect
  2801.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  2802.     and.b    #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb    ;select all drives
  2803.     bsr    Deselect
  2804.     clr.b    MotorState(A_DEVICE)
  2805.     rts
  2806.  
  2807. Inquire:
  2808.     IFND    _LVOGetUnitID
  2809. _LVOGetUnitID    EQU    -30
  2810.     ENDC
  2811.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2812.     moveq    #0,d2
  2813.     move.l    DiskResourceBase(A_DEVICE),a6
  2814. .Loop:    move.l    d2,d0
  2815.     SYS    GetUnitID
  2816.     moveq    #-1,d1
  2817.     cmp.l    d0,d1
  2818.     beq    .Next
  2819.     bset    d2,InquireBits(A_DEVICE)
  2820. .Next:    addq.l    #1,d2
  2821.     cmp.w    #MD_NUMUNITS,d2
  2822.     blo    .Loop
  2823.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2824.     rts
  2825.  
  2826. GetDriveType:
  2827.  
  2828. ;Determines type of drive and places the proper code into DriveType(A_UNIT).
  2829. ;One of: DRIVE3_5, DRIVE5_25, DRIVE3_5_150RPM
  2830.  
  2831. ;See the Hardware Reference Manual, Appendix E for a description of
  2832. ;the procedure.
  2833.  
  2834. ;Note: Drive zero is always present. In fact, the identification 
  2835. ;scheme does not work with drive zero (except for the half-speed drive).
  2836.  
  2837. ;Warning! Leaves drive deselected!
  2838.  
  2839.     movem.l    d0-d1/d3-d5/a0-a2/a6,-(sp)
  2840.     move.b    UnitNum(A_UNIT),d3
  2841.     addq.b    #3,d3
  2842.     moveq    #7,d1
  2843.     move.l    #ciabprb,a0
  2844.     bsr    Deselect
  2845.     moveq    #31,d4
  2846.     moveq    #0,d5    ;identification longword
  2847.  
  2848.     bclr    d1,(a0)    ;motor on
  2849.     bset    d3,(a0)    ;deselect drive
  2850.     bclr    d3,(a0)    ;select drive
  2851.  
  2852.     bset    d1,(a0)    ;motor off
  2853.     bset    d3,(a0)    ;deselect drive
  2854.     bclr    d3,(a0)    ;select drive
  2855.     bset    d3,(a0)    ;deselect drive
  2856.  
  2857. .ReadIdent:
  2858.     bclr    d3,(a0)    ;select drive
  2859.     btst    #5,ciaapra    ;test ready
  2860.     beq    .zero
  2861.     bset    d4,d5
  2862. .zero:    bset    d3,(a0)    ;deselect drive
  2863.     dbra    d4,.ReadIdent
  2864.  
  2865. ;D5 contains the drive ID.
  2866.     lea    DriveParams(A_UNIT),a0
  2867.     move.w    #(DriveParams_Sizeof/2)-1,d0
  2868. ..    clr.w    (a0)+
  2869.     dbra    d0,..
  2870.     lea    HDParams(pc),a1
  2871.     cmp.l    #DRT_150RPM,d5
  2872.     beq    .DoInitStruct
  2873.     lea    LDParams(pc),a1
  2874. ;    cmp.l    #DRT_AMIGA,d5
  2875.     tst.l    d5
  2876.     beq    .DoInitStruct
  2877.     moveq    #-1,d0
  2878.     cmp.l    d0,d5
  2879.     beq    .DoInitStruct
  2880.     lea    OldParams(pc),a1    ;5.25 inch drive
  2881. .DoInitStruct:
  2882.     moveq    #0,d0    ;area to clear
  2883.     move.l    A_UNIT,a2
  2884.     move.l    4,a6
  2885.     SYS    InitStruct
  2886. .End:    movem.l    (sp)+,d0-d1/d3-d5/a0-a2/a6
  2887.     rts
  2888.  
  2889.  
  2890. SeekZeroAll:
  2891.     movem.l    d0-d2/A_UNIT,-(sp)
  2892.     move.b    InquireBits(A_DEVICE),d0
  2893.     lea    Unit0(A_DEVICE),A_UNIT
  2894.     moveq    #0,d1
  2895.     moveq    #MD_NUMUNITS-1,d2
  2896. .Loop:    btst    d1,d0
  2897.     beq    .Next
  2898.     bsr    SeekZero
  2899. .Next:    lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  2900.     addq.b    #1,d1
  2901.     dbra    d2,.Loop
  2902.     movem.l    (sp)+,d0-d2/A_UNIT
  2903.     rts
  2904.  
  2905. SeekZero:
  2906. ;Places the drive in UnitNum(A_UNIT) on track zero.
  2907. ;Drive does not need to be selected in advance.
  2908. ;Drive will be left _deselected_!
  2909.  
  2910.     movem.l    d0-d1,-(sp)
  2911.  
  2912. .StepLoop:
  2913.     bsr    SelectDriveSameMotor
  2914.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  2915.     btst    #CIAB_DSKTRACK0,ciaapra    ;check track zero flag
  2916.     beq    .EndStepLoop
  2917.     bset    #CIAB_DSKSTEP,ciabprb
  2918.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  2919.     bset    #CIAB_DSKSTEP,ciabprb
  2920.     bsr    Deselect
  2921.     move.l    TDU_CALIBRATEDELAY(A_UNIT),d0
  2922.     bsr    delay
  2923.     bra    .StepLoop
  2924.  
  2925. .EndStepLoop:
  2926.     bsr    Deselect
  2927.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  2928.     bsr    delay
  2929.     clr.w    TDU_CURRTRK(A_UNIT)
  2930.     movem.l    (sp)+,d0-d1
  2931.     rts
  2932.  
  2933. CopyMemSlow:
  2934. ;Only to be called by CopyMem
  2935.     move.l    4,a6
  2936.     SYS    CopyMem
  2937.     movem.l    (sp)+,d0-d7/a0-a6
  2938.     rts
  2939.  
  2940. CopyMem:
  2941.  
  2942. ;A0 - source
  2943. ;A1 - destination
  2944. ;D0 - size
  2945.  
  2946.     movem.l    d0-d7/a0-a6,-(sp)
  2947.  
  2948.     move.l    a0,d1
  2949.     btst    #0,d1
  2950.     bne    CopyMemSlow
  2951.     move.l    a1,d1
  2952.     btst    #0,d1
  2953.     bne    CopyMemSlow
  2954.  
  2955.  
  2956. .More:    cmp.l    #512,d0
  2957.     blo    CopyMemSlow
  2958.  
  2959. ;Copy 480 bytes
  2960. n    set    0
  2961.     REPT    10
  2962.     movem.l    (a0)+,d1-d7/a2-a6
  2963.     movem.l    d1-d7/a2-a6,n*48(a1)
  2964. n    set    n+1
  2965.     ENDR
  2966. ;Copy 32 bytes
  2967.     movem.l    (a0)+,d1-d7/a2
  2968.     movem.l    d1-d7/a2,480(a1)
  2969.     lea    512(a1),a1
  2970.     sub.l    #512,d0
  2971.     bne    .More
  2972.     movem.l    (sp)+,d0-d7/a0-a6
  2973.     rts
  2974.  
  2975. LDParams:
  2976.     INITWORD    TDU_COMP10TRACK,-1
  2977.     INITWORD    TDU_COMP11TRACK,-1
  2978.     INITWORD    TDU_COMP01TRACK,NUMTRACKS
  2979.     INITLONG    TDU_STEPDELAY,3*1000
  2980.     INITLONG    TDU_SETTLEDELAY,15*1000
  2981.     INITLONG    TDU_CALIBRATEDELAY,4*1000
  2982.  
  2983.     INITBYTE    NumTracks,NUMTRACKS*2
  2984.     INITBYTE    DriveType,DRIVE3_5
  2985.     INITLONG    BytesPerDisk,2*SECPERTRACK*512*NUMTRACKS
  2986.     INITLONG    SectorMask,%11111111111
  2987.     INITWORD    GapCount,414
  2988.     INITLONG    SectorsPerTrack,SECPERTRACK
  2989.     INITLONG    RawBufSize,13630
  2990.  
  2991.     INITWORD    FirstSector,1666
  2992.     INITWORD    MaxValidSec,$0a00
  2993.     INITWORD    WriteDskLen,$da9e
  2994.     INITWORD    ReadDskLen,$9a9e
  2995.     INITWORD    VerifyDskLen,$9761
  2996.     dc.w    0
  2997.  
  2998. OldParams:    ;for 5.25 inch drives
  2999.     INITWORD    TDU_COMP10TRACK,-1
  3000.     INITWORD    TDU_COMP11TRACK,-1
  3001.     INITWORD    TDU_COMP01TRACK,NUMTRACKS/2
  3002.     INITLONG    TDU_STEPDELAY,3*1000*2
  3003.     INITLONG    TDU_SETTLEDELAY,15*1000*2
  3004.     INITLONG    TDU_CALIBRATEDELAY,4*1000*2
  3005.  
  3006.     INITBYTE    NumTracks,NUMTRACKS
  3007.     INITBYTE    DriveType,DRIVE5_25
  3008.     INITLONG    BytesPerDisk,2*SECPERTRACK*512*NUMTRACKS/2
  3009.     INITLONG    SectorMask,%11111111111
  3010.     INITWORD    GapCount,414
  3011.     INITLONG    SectorsPerTrack,SECPERTRACK
  3012.     INITLONG    RawBufSize,13630
  3013.  
  3014.     INITWORD    FirstSector,1666
  3015.     INITWORD    MaxValidSec,$0a00
  3016.     INITWORD    WriteDskLen,$da9e
  3017.     INITWORD    ReadDskLen,$9a9e
  3018.     INITWORD    VerifyDskLen,$9761
  3019.     dc.w    0
  3020.  
  3021. HDParams:
  3022.     INITWORD    TDU_COMP10TRACK,-1
  3023.     INITWORD    TDU_COMP11TRACK,-1
  3024.     INITWORD    TDU_COMP01TRACK,NUMTRACKS
  3025.     INITLONG    TDU_STEPDELAY,3*1000
  3026.     INITLONG    TDU_SETTLEDELAY,15*1000
  3027.     INITLONG    TDU_CALIBRATEDELAY,4*1000
  3028.  
  3029.     INITBYTE    NumTracks,NUMTRACKS*2
  3030.     INITBYTE    DriveType,DRIVE3_5_150RPM
  3031.     INITLONG    BytesPerDisk,2*SECPERTRACK*512*NUMTRACKS*2
  3032.     INITLONG    SectorMask,%1111111111111111111111
  3033.     INITWORD    GapCount,829
  3034.     INITLONG    SectorsPerTrack,SECPERTRACK*2
  3035.     INITLONG    RawBufSize,13630*2
  3036.  
  3037.     INITWORD    FirstSector,3326
  3038.     INITWORD    MaxValidSec,$1500
  3039.     INITWORD    WriteDskLen,$f53c
  3040.     INITWORD    ReadDskLen,$b53b
  3041.     INITWORD    VerifyDskLen,$aec1
  3042.     dc.w    0
  3043.  
  3044. ENDCode:
  3045.  
  3046. ;************************* Northgate fix *********************************
  3047.  
  3048. ;This tiny module performs a keyboard handshake. Its purpose is to wake up a
  3049. ;Northgate keyboard on accelerated systems. If you don't fit that description
  3050. ;then just ignore it. It's harmless.
  3051.  
  3052. NRomTag:    dc.w    RTC_MATCHWORD    ;$4AFC ('illegal' opcode)
  3053.     dc.l    NRomTag
  3054.     dc.l    NENDCode    ;pointer to end of code
  3055.     dc.b    RTF_COLDSTART
  3056.     dc.b    0    ;version
  3057.     dc.b    NT_LIBRARY    ;module type (either device or library)
  3058.     dc.b    0    ;priority
  3059.     dc.l    FixName    ;name
  3060.     dc.l    FixName    ;IDString
  3061.     dc.l    Northgate    ;init routine
  3062. FixName:    dc.b    'Northgate_fix',$a,0
  3063.     even
  3064.  
  3065. Northgate:
  3066. ;Perform a keyboard handshake
  3067.     or.b    #$40,$BFEE01
  3068.     clr.b    $bfec01
  3069.     move.l    #$bfe001,a0
  3070.     move.w    #$46*3,d0
  3071. ..    move.b    (a0),(a0)
  3072.     dbra    d0,..
  3073.     and.b    #$BF,$BFEE01
  3074.     rts
  3075.  
  3076. NENDCode:
  3077.  
  3078. ;*************************** Debugging stuff *************************
  3079.  
  3080.     IFNE    INFO_LEVEL
  3081.  
  3082. KPutFmt:    move.l    a2,-(sp)
  3083.     lea    KPutChar(pc),a2
  3084.     bsr    KDoFmt
  3085.     move.l    (sp)+,a2
  3086.     rts
  3087.  
  3088. KDoFmt:    move.l    a6,-(sp)
  3089.     move.l    4,a6
  3090.     SYS    RawDoFmt
  3091.     move.l    (sp)+,a6
  3092.     rts
  3093.  
  3094. KPutChar:
  3095.  
  3096. ;Serial
  3097.     IFGT    0
  3098.     move.l    a6,-(sp)
  3099.     move.l    4,a6
  3100.     SYS    RawPutChar
  3101.     move.l    (sp)+,a6
  3102.     rts
  3103.     ENDC
  3104.  
  3105. ;Printer
  3106.     IFGT    0
  3107.     move.b    #$ff,$bfe301
  3108. .Print:    btst    #0,$bfd000
  3109.     bne    .Print
  3110.     move.b    d0,$bfe101
  3111.     rts
  3112.     ENDC
  3113.  
  3114. ;Memory
  3115.     tst.l    MemPtr
  3116.     bne    .OK
  3117.     move.l    #$500000,MemPtr
  3118. .OK:
  3119.     push    a0
  3120.     move.l    MemPtr(pc),a0
  3121.     move.b    d0,(a0)+
  3122.     move.l    a0,MemPtr
  3123.     pop    a0
  3124.     rts
  3125.  
  3126. MemPtr:    dc.l    0
  3127.  
  3128.     ENDC
  3129.